1*b471b82fSyamt /* $NetBSD: ieee80211_input.c,v 1.117 2022/11/19 07:57:51 yamt Exp $ */
26d2a143aSmaxv
36d2a143aSmaxv /*
440e261aaSdyoung * Copyright (c) 2001 Atsushi Onoe
590634029Sdyoung * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
640e261aaSdyoung * All rights reserved.
740e261aaSdyoung *
840e261aaSdyoung * Redistribution and use in source and binary forms, with or without
940e261aaSdyoung * modification, are permitted provided that the following conditions
1040e261aaSdyoung * are met:
1140e261aaSdyoung * 1. Redistributions of source code must retain the above copyright
1240e261aaSdyoung * notice, this list of conditions and the following disclaimer.
1340e261aaSdyoung * 2. Redistributions in binary form must reproduce the above copyright
1440e261aaSdyoung * notice, this list of conditions and the following disclaimer in the
1540e261aaSdyoung * documentation and/or other materials provided with the distribution.
1640e261aaSdyoung * 3. The name of the author may not be used to endorse or promote products
1740e261aaSdyoung * derived from this software without specific prior written permission.
1840e261aaSdyoung *
1940e261aaSdyoung * Alternatively, this software may be distributed under the terms of the
2040e261aaSdyoung * GNU General Public License ("GPL") version 2 as published by the Free
2140e261aaSdyoung * Software Foundation.
2240e261aaSdyoung *
2340e261aaSdyoung * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2440e261aaSdyoung * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2540e261aaSdyoung * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2640e261aaSdyoung * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2740e261aaSdyoung * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2840e261aaSdyoung * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2940e261aaSdyoung * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3040e261aaSdyoung * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3140e261aaSdyoung * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3240e261aaSdyoung * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3340e261aaSdyoung */
3440e261aaSdyoung
3540e261aaSdyoung #include <sys/cdefs.h>
366e8d0b71Sdyoung #ifdef __FreeBSD__
3787515e34Sskrll __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.81 2005/08/10 16:22:29 sam Exp $");
3890634029Sdyoung #endif
3990634029Sdyoung #ifdef __NetBSD__
40*b471b82fSyamt __KERNEL_RCSID(0, "$NetBSD: ieee80211_input.c,v 1.117 2022/11/19 07:57:51 yamt Exp $");
416e8d0b71Sdyoung #endif
4240e261aaSdyoung
431c4a50f1Spooka #ifdef _KERNEL_OPT
4440e261aaSdyoung #include "opt_inet.h"
451c4a50f1Spooka #endif
4640e261aaSdyoung
4740e261aaSdyoung #include <sys/param.h>
4840e261aaSdyoung #include <sys/systm.h>
4940e261aaSdyoung #include <sys/mbuf.h>
5040e261aaSdyoung #include <sys/malloc.h>
5190634029Sdyoung #include <sys/endian.h>
5240e261aaSdyoung #include <sys/kernel.h>
5390634029Sdyoung
5440e261aaSdyoung #include <sys/socket.h>
5540e261aaSdyoung #include <sys/sockio.h>
5640e261aaSdyoung #include <sys/endian.h>
5740e261aaSdyoung #include <sys/errno.h>
5840e261aaSdyoung #include <sys/proc.h>
5940e261aaSdyoung #include <sys/sysctl.h>
605ffb0503Snonaka #include <sys/cpu.h>
6140e261aaSdyoung
6240e261aaSdyoung #include <net/if.h>
6340e261aaSdyoung #include <net/if_media.h>
6440e261aaSdyoung #include <net/if_arp.h>
65c059ea00Sdyoung #include <net/if_ether.h>
6640e261aaSdyoung #include <net/if_llc.h>
6740e261aaSdyoung
6840e261aaSdyoung #include <net80211/ieee80211_var.h>
6940e261aaSdyoung
7040e261aaSdyoung #include <net/bpf.h>
7140e261aaSdyoung
7240e261aaSdyoung #ifdef INET
7340e261aaSdyoung #include <netinet/in.h>
74c059ea00Sdyoung #include <net/if_ether.h>
75c059ea00Sdyoung #endif
7640e261aaSdyoung
77ce488d2bSdyoung const struct timeval ieee80211_merge_print_intvl = {.tv_sec = 1, .tv_usec = 0};
78ce488d2bSdyoung
79a02b46e8Smycroft #ifdef IEEE80211_DEBUG
8090634029Sdyoung
81a02b46e8Smycroft /*
82a02b46e8Smycroft * Decide if a received management frame should be
83a02b46e8Smycroft * printed when debugging is enabled. This filters some
84a02b46e8Smycroft * of the less interesting frames that come frequently
85a02b46e8Smycroft * (e.g. beacons).
86a02b46e8Smycroft */
87a02b46e8Smycroft static __inline int
doprint(struct ieee80211com * ic,int subtype)88a02b46e8Smycroft doprint(struct ieee80211com *ic, int subtype)
89a02b46e8Smycroft {
90a02b46e8Smycroft switch (subtype) {
91a02b46e8Smycroft case IEEE80211_FC0_SUBTYPE_BEACON:
9290634029Sdyoung return (ic->ic_flags & IEEE80211_F_SCAN);
93a02b46e8Smycroft case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
94a02b46e8Smycroft return (ic->ic_opmode == IEEE80211_M_IBSS);
95a02b46e8Smycroft }
96a02b46e8Smycroft return 1;
97a02b46e8Smycroft }
9890634029Sdyoung
9990634029Sdyoung /*
10090634029Sdyoung * Emit a debug message about discarding a frame or information
10190634029Sdyoung * element. One format is for extracting the mac address from
10290634029Sdyoung * the frame header; the other is for when a header is not
10390634029Sdyoung * available or otherwise appropriate.
10490634029Sdyoung */
10590634029Sdyoung #define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \
10690634029Sdyoung if ((_ic)->ic_debug & (_m)) \
10790634029Sdyoung ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\
10890634029Sdyoung } while (0)
10990634029Sdyoung #define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \
11090634029Sdyoung if ((_ic)->ic_debug & (_m)) \
11190634029Sdyoung ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\
11290634029Sdyoung } while (0)
11390634029Sdyoung #define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \
11490634029Sdyoung if ((_ic)->ic_debug & (_m)) \
11590634029Sdyoung ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\
11690634029Sdyoung } while (0)
117c2ec5838Schristos #define IEEE80211_DEBUGVAR(a) a
11890634029Sdyoung
11990634029Sdyoung static const u_int8_t *ieee80211_getbssid(struct ieee80211com *,
12090634029Sdyoung const struct ieee80211_frame *);
12190634029Sdyoung static void ieee80211_discard_frame(struct ieee80211com *,
12290634029Sdyoung const struct ieee80211_frame *, const char *type, const char *fmt, ...);
12390634029Sdyoung static void ieee80211_discard_ie(struct ieee80211com *,
12490634029Sdyoung const struct ieee80211_frame *, const char *type, const char *fmt, ...);
12590634029Sdyoung static void ieee80211_discard_mac(struct ieee80211com *,
12690634029Sdyoung const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type,
12790634029Sdyoung const char *fmt, ...);
12890634029Sdyoung #else
12990634029Sdyoung #define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...)
13090634029Sdyoung #define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...)
13190634029Sdyoung #define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...)
132c2ec5838Schristos #define IEEE80211_DEBUGVAR(a)
13390634029Sdyoung #endif /* IEEE80211_DEBUG */
13490634029Sdyoung
1351f639d06Smaxv static struct mbuf *ieee80211_defrag(struct ieee80211_node *,
1361f639d06Smaxv struct mbuf *, int);
1371f639d06Smaxv static struct mbuf *ieee80211_decap(struct mbuf *, int);
13880b91eeaSdyoung static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
13980b91eeaSdyoung const u_int8_t *mac, int subtype, int arg);
14087515e34Sskrll static void ieee80211_deliver_data(struct ieee80211com *,
14187515e34Sskrll struct ieee80211_node *, struct mbuf *);
14225e9e914Sdyoung #ifndef IEEE80211_NO_HOSTAP
14390634029Sdyoung static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
14490634029Sdyoung static void ieee80211_recv_pspoll(struct ieee80211com *,
14590634029Sdyoung struct ieee80211_node *, struct mbuf *);
14674988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
1477b6a25edSdyoung static void ieee80211_update_adhoc_node(struct ieee80211com *,
1487b6a25edSdyoung struct ieee80211_node *, struct ieee80211_frame *,
1497b6a25edSdyoung struct ieee80211_scanparams *, int, u_int32_t);
150a02b46e8Smycroft
1511151deddSmaxv /* -------------------------------------------------------------------------- */
1521151deddSmaxv
15340e261aaSdyoung /*
1541151deddSmaxv * Input code for a DATA frame.
15540e261aaSdyoung */
1561151deddSmaxv static int
ieee80211_input_data(struct ieee80211com * ic,struct mbuf ** mp,struct ieee80211_node * ni)1571151deddSmaxv ieee80211_input_data(struct ieee80211com *ic, struct mbuf **mp,
1581151deddSmaxv struct ieee80211_node *ni)
15940e261aaSdyoung {
16090634029Sdyoung struct ifnet *ifp = ic->ic_ifp;
16190634029Sdyoung struct ieee80211_key *key;
1621151deddSmaxv struct ieee80211_frame *wh;
1631151deddSmaxv u_int8_t dir, subtype;
16440e261aaSdyoung struct ether_header *eh;
1651151deddSmaxv struct mbuf *m = *mp;
16680b91eeaSdyoung int hdrspace;
16740e261aaSdyoung
16840e261aaSdyoung wh = mtod(m, struct ieee80211_frame *);
16940e261aaSdyoung dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
17090634029Sdyoung subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
1716d2a143aSmaxv
17280b91eeaSdyoung hdrspace = ieee80211_hdrspace(ic, wh);
1731151deddSmaxv
17480b91eeaSdyoung if (m->m_len < hdrspace &&
17580b91eeaSdyoung (m = m_pullup(m, hdrspace)) == NULL) {
17690634029Sdyoung ic->ic_stats.is_rx_tooshort++;
17701f382e3Smaxv goto out;
17890634029Sdyoung }
179e63bca16Smaxv wh = mtod(m, struct ieee80211_frame *);
180e63bca16Smaxv
18140e261aaSdyoung switch (ic->ic_opmode) {
18240e261aaSdyoung case IEEE80211_M_STA:
1839280f4b4Sdyoung if (dir != IEEE80211_FC1_DIR_FROMDS) {
18487515e34Sskrll IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
185*b471b82fSyamt wh, "data", "unknown dir 0x%x", dir);
1869280f4b4Sdyoung ic->ic_stats.is_rx_wrongdir++;
18740e261aaSdyoung goto out;
1889280f4b4Sdyoung }
18940e261aaSdyoung if ((ifp->if_flags & IFF_SIMPLEX) &&
19040e261aaSdyoung IEEE80211_IS_MULTICAST(wh->i_addr1) &&
19140e261aaSdyoung IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
19240e261aaSdyoung /*
19340e261aaSdyoung * In IEEE802.11 network, multicast packet
1946d2a143aSmaxv * sent from me is broadcast from AP.
19540e261aaSdyoung * It should be silently discarded for
19640e261aaSdyoung * SIMPLEX interface.
19740e261aaSdyoung */
19890634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
19990634029Sdyoung wh, NULL, "%s", "multicast echo");
2009280f4b4Sdyoung ic->ic_stats.is_rx_mcastecho++;
20140e261aaSdyoung goto out;
20240e261aaSdyoung }
20340e261aaSdyoung break;
2046d2a143aSmaxv
20540e261aaSdyoung case IEEE80211_M_IBSS:
20640e261aaSdyoung case IEEE80211_M_AHDEMO:
2079280f4b4Sdyoung if (dir != IEEE80211_FC1_DIR_NODS) {
20887515e34Sskrll IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
209*b471b82fSyamt wh, "data", "unknown dir 0x%x", dir);
2109280f4b4Sdyoung ic->ic_stats.is_rx_wrongdir++;
21140e261aaSdyoung goto out;
2129280f4b4Sdyoung }
21390634029Sdyoung /* XXX no power-save support */
21440e261aaSdyoung break;
2156d2a143aSmaxv
21640e261aaSdyoung case IEEE80211_M_HOSTAP:
21774988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
2189280f4b4Sdyoung if (dir != IEEE80211_FC1_DIR_TODS) {
21987515e34Sskrll IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
220*b471b82fSyamt wh, "data", "unknown dir 0x%x", dir);
2219280f4b4Sdyoung ic->ic_stats.is_rx_wrongdir++;
22240e261aaSdyoung goto out;
2239280f4b4Sdyoung }
22440e261aaSdyoung /* check if source STA is associated */
22540e261aaSdyoung if (ni == ic->ic_bss) {
22690634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
22790634029Sdyoung wh, "data", "%s", "unknown src");
22880b91eeaSdyoung ieee80211_send_error(ic, ni, wh->i_addr2,
22940e261aaSdyoung IEEE80211_FC0_SUBTYPE_DEAUTH,
23040e261aaSdyoung IEEE80211_REASON_NOT_AUTHED);
2319280f4b4Sdyoung ic->ic_stats.is_rx_notassoc++;
23240e261aaSdyoung goto err;
23340e261aaSdyoung }
23440e261aaSdyoung if (ni->ni_associd == 0) {
23590634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
23690634029Sdyoung wh, "data", "%s", "unassoc src");
23740e261aaSdyoung IEEE80211_SEND_MGMT(ic, ni,
23840e261aaSdyoung IEEE80211_FC0_SUBTYPE_DISASSOC,
23940e261aaSdyoung IEEE80211_REASON_NOT_ASSOCED);
2409280f4b4Sdyoung ic->ic_stats.is_rx_notassoc++;
24140e261aaSdyoung goto err;
24240e261aaSdyoung }
24390634029Sdyoung
24490634029Sdyoung /*
24590634029Sdyoung * Check for power save state change.
24690634029Sdyoung */
24790634029Sdyoung if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
24890634029Sdyoung (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
24990634029Sdyoung ieee80211_node_pwrsave(ni,
25090634029Sdyoung wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
25174988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
25240e261aaSdyoung break;
2536d2a143aSmaxv
25490634029Sdyoung default:
25590634029Sdyoung /* XXX here to keep compiler happy */
25690634029Sdyoung goto out;
25740e261aaSdyoung }
25890634029Sdyoung
25990634029Sdyoung /*
26090634029Sdyoung * Handle privacy requirements. Note that we
26190634029Sdyoung * must not be preempted from here until after
26290634029Sdyoung * we (potentially) call ieee80211_crypto_demic;
26390634029Sdyoung * otherwise we may violate assumptions in the
26490634029Sdyoung * crypto cipher modules used to do delayed update
26590634029Sdyoung * of replay sequence numbers.
26690634029Sdyoung */
26740e261aaSdyoung if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
26890634029Sdyoung if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
26990634029Sdyoung /*
27090634029Sdyoung * Discard encrypted frames when privacy is off.
27190634029Sdyoung */
27290634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
27390634029Sdyoung wh, "WEP", "%s", "PRIVACY off");
27490634029Sdyoung ic->ic_stats.is_rx_noprivacy++;
27590634029Sdyoung IEEE80211_NODE_STAT(ni, rx_noprivacy);
27690634029Sdyoung goto out;
27790634029Sdyoung }
2780b0687d8Smaxv key = ieee80211_crypto_decap(ic, ni, &m, hdrspace);
27990634029Sdyoung if (key == NULL) {
28090634029Sdyoung /* NB: stats+msgs handled in crypto_decap */
28190634029Sdyoung IEEE80211_NODE_STAT(ni, rx_wepfail);
28290634029Sdyoung goto out;
2839280f4b4Sdyoung }
28440e261aaSdyoung wh = mtod(m, struct ieee80211_frame *);
28590634029Sdyoung wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
2869280f4b4Sdyoung } else {
28790634029Sdyoung key = NULL;
28890634029Sdyoung }
28990634029Sdyoung
29090634029Sdyoung /*
29190634029Sdyoung * Next up, any fragmentation.
29290634029Sdyoung */
29390634029Sdyoung if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
2941f639d06Smaxv m = ieee80211_defrag(ni, m, hdrspace);
29590634029Sdyoung if (m == NULL) {
29690634029Sdyoung /* Fragment dropped or frame not complete yet */
29740e261aaSdyoung goto out;
29840e261aaSdyoung }
2999280f4b4Sdyoung }
30090634029Sdyoung
30190634029Sdyoung /*
30201f382e3Smaxv * Next, strip any MSDU crypto bits.
30390634029Sdyoung */
30480b91eeaSdyoung if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
30590634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
30690634029Sdyoung ni->ni_macaddr, "data", "%s", "demic error");
30790634029Sdyoung IEEE80211_NODE_STAT(ni, rx_demicfail);
30890634029Sdyoung goto out;
30990634029Sdyoung }
31090634029Sdyoung
31140e261aaSdyoung /* copy to listener after decrypt */
3123cd62456Smsaitoh bpf_mtap3(ic->ic_rawbpf, m, BPF_D_IN);
31390634029Sdyoung
31490634029Sdyoung /*
31590634029Sdyoung * Finally, strip the 802.11 header.
31690634029Sdyoung */
3171f639d06Smaxv m = ieee80211_decap(m, hdrspace);
3184232da0fSdyoung if (m == NULL) {
31990634029Sdyoung /* don't count Null data frames as errors */
32090634029Sdyoung if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
32190634029Sdyoung goto out;
32290634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
32390634029Sdyoung ni->ni_macaddr, "data", "%s", "decap error");
3249280f4b4Sdyoung ic->ic_stats.is_rx_decap++;
32590634029Sdyoung IEEE80211_NODE_STAT(ni, rx_decap);
32640e261aaSdyoung goto err;
3274232da0fSdyoung }
3286d2a143aSmaxv
32990634029Sdyoung eh = mtod(m, struct ether_header *);
33090634029Sdyoung if (!ieee80211_node_is_authorized(ni)) {
33190634029Sdyoung /*
33290634029Sdyoung * Deny any non-PAE frames received prior to
33390634029Sdyoung * authorization. For open/shared-key
33490634029Sdyoung * authentication the port is mark authorized
33590634029Sdyoung * after authentication completes. For 802.1x
33690634029Sdyoung * the port is not marked authorized by the
33790634029Sdyoung * authenticator until the handshake has completed.
33890634029Sdyoung */
33990634029Sdyoung if (eh->ether_type != htons(ETHERTYPE_PAE)) {
34090634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
34190634029Sdyoung eh->ether_shost, "data",
34290634029Sdyoung "unauthorized port: ether type 0x%x len %u",
34390634029Sdyoung eh->ether_type, m->m_pkthdr.len);
34490634029Sdyoung ic->ic_stats.is_rx_unauth++;
34590634029Sdyoung IEEE80211_NODE_STAT(ni, rx_unauth);
34690634029Sdyoung goto err;
34790634029Sdyoung }
34890634029Sdyoung } else {
34990634029Sdyoung /*
35090634029Sdyoung * When denying unencrypted frames, discard
35190634029Sdyoung * any non-PAE frames received without encryption.
35290634029Sdyoung */
35390634029Sdyoung if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
354a45976c2Smaxv key == NULL && eh->ether_type != htons(ETHERTYPE_PAE)) {
35590634029Sdyoung /*
35690634029Sdyoung * Drop unencrypted frames.
35790634029Sdyoung */
35890634029Sdyoung ic->ic_stats.is_rx_unencrypted++;
35990634029Sdyoung IEEE80211_NODE_STAT(ni, rx_unencrypted);
36090634029Sdyoung goto out;
36190634029Sdyoung }
36290634029Sdyoung }
3636d2a143aSmaxv
3641a2cab17Sthorpej if_statinc(ifp, if_ipackets);
36590634029Sdyoung IEEE80211_NODE_STAT(ni, rx_data);
36690634029Sdyoung IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
36740e261aaSdyoung
36887515e34Sskrll ieee80211_deliver_data(ic, ni, m);
36940e261aaSdyoung
3701151deddSmaxv *mp = NULL;
3711151deddSmaxv return 0;
3721151deddSmaxv
3731151deddSmaxv err:
3741a2cab17Sthorpej if_statinc(ifp, if_ierrors);
3751151deddSmaxv out:
3761151deddSmaxv *mp = m;
3771151deddSmaxv return -1;
3781151deddSmaxv }
3791151deddSmaxv
3801151deddSmaxv /*
3811151deddSmaxv * Input code for a MANAGEMENT frame.
3821151deddSmaxv */
3831151deddSmaxv static int
ieee80211_input_management(struct ieee80211com * ic,struct mbuf ** mp,struct ieee80211_node * ni,int rssi,u_int32_t rstamp)3841151deddSmaxv ieee80211_input_management(struct ieee80211com *ic, struct mbuf **mp,
3851151deddSmaxv struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
3861151deddSmaxv {
3871151deddSmaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
3881151deddSmaxv struct ifnet *ifp = ic->ic_ifp;
3891151deddSmaxv struct ieee80211_key *key;
3901151deddSmaxv struct ieee80211_frame *wh;
3911151deddSmaxv u_int8_t dir, subtype;
3921151deddSmaxv struct mbuf *m = *mp;
3931151deddSmaxv int hdrspace;
3941151deddSmaxv
3951151deddSmaxv wh = mtod(m, struct ieee80211_frame *);
3961151deddSmaxv dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
3971151deddSmaxv subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
3981151deddSmaxv
39990634029Sdyoung IEEE80211_NODE_STAT(ni, rx_mgmt);
4009280f4b4Sdyoung if (dir != IEEE80211_FC1_DIR_NODS) {
40187515e34Sskrll IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
402*b471b82fSyamt wh, "data", "unknown dir 0x%x", dir);
4039280f4b4Sdyoung ic->ic_stats.is_rx_wrongdir++;
40440e261aaSdyoung goto err;
4059280f4b4Sdyoung }
406a45976c2Smaxv if (m->m_len < sizeof(struct ieee80211_frame)) {
407a45976c2Smaxv IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY, ni->ni_macaddr,
408a45976c2Smaxv "mgt", "too short: len %u", m->m_len);
40990634029Sdyoung ic->ic_stats.is_rx_tooshort++;
41040e261aaSdyoung goto out;
4119280f4b4Sdyoung }
41240e261aaSdyoung #ifdef IEEE80211_DEBUG
413a02b46e8Smycroft if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
414a02b46e8Smycroft ieee80211_msg_dumppkts(ic)) {
41590634029Sdyoung if_printf(ic->ic_ifp, "received %s from %s rssi %d\n",
41690634029Sdyoung ieee80211_mgt_subtype_name[subtype >>
41790634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT],
418c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
419c2ec5838Schristos rssi);
42040e261aaSdyoung }
421a02b46e8Smycroft #endif
4221151deddSmaxv
42390634029Sdyoung if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
42490634029Sdyoung if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
42590634029Sdyoung /*
42690634029Sdyoung * Only shared key auth frames with a challenge
42790634029Sdyoung * should be encrypted, discard all others.
42890634029Sdyoung */
42990634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
43090634029Sdyoung wh, ieee80211_mgt_subtype_name[subtype >>
43190634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT],
43290634029Sdyoung "%s", "WEP set but not permitted");
43390634029Sdyoung ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
43490634029Sdyoung goto out;
43590634029Sdyoung }
43690634029Sdyoung if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
43790634029Sdyoung /*
43890634029Sdyoung * Discard encrypted frames when privacy is off.
43990634029Sdyoung */
44090634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
44190634029Sdyoung wh, "mgt", "%s", "WEP set but PRIVACY off");
44290634029Sdyoung ic->ic_stats.is_rx_noprivacy++;
44390634029Sdyoung goto out;
44490634029Sdyoung }
44580b91eeaSdyoung hdrspace = ieee80211_hdrspace(ic, wh);
4460b0687d8Smaxv key = ieee80211_crypto_decap(ic, ni, &m, hdrspace);
44790634029Sdyoung if (key == NULL) {
44890634029Sdyoung /* NB: stats+msgs handled in crypto_decap */
44990634029Sdyoung goto out;
45090634029Sdyoung }
45190634029Sdyoung wh = mtod(m, struct ieee80211_frame *);
45290634029Sdyoung wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
45390634029Sdyoung }
4546d2a143aSmaxv
4553cd62456Smsaitoh bpf_mtap3(ic->ic_rawbpf, m, BPF_D_IN);
45640e261aaSdyoung (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
45740e261aaSdyoung m_freem(m);
45840e261aaSdyoung
4591151deddSmaxv *mp = NULL;
4601151deddSmaxv return 0;
4611151deddSmaxv
4621151deddSmaxv err:
4631a2cab17Sthorpej if_statinc(ifp, if_ierrors);
4641151deddSmaxv out:
4651151deddSmaxv *mp = m;
4661151deddSmaxv return -1;
4671151deddSmaxv }
4681151deddSmaxv
4691151deddSmaxv /*
4701151deddSmaxv * Input code for a CONTROL frame.
4711151deddSmaxv */
4721151deddSmaxv static void
ieee80211_input_control(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)4731151deddSmaxv ieee80211_input_control(struct ieee80211com *ic, struct mbuf *m,
4741151deddSmaxv struct ieee80211_node *ni)
4751151deddSmaxv {
47690634029Sdyoung IEEE80211_NODE_STAT(ni, rx_ctrl);
4779280f4b4Sdyoung ic->ic_stats.is_rx_ctl++;
4781151deddSmaxv
47974988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
48090634029Sdyoung if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
4811151deddSmaxv struct ieee80211_frame *wh;
4821151deddSmaxv u_int8_t subtype;
4831151deddSmaxv
4841151deddSmaxv wh = mtod(m, struct ieee80211_frame *);
4851151deddSmaxv subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
4861151deddSmaxv
48790634029Sdyoung switch (subtype) {
48890634029Sdyoung case IEEE80211_FC0_SUBTYPE_PS_POLL:
48990634029Sdyoung ieee80211_recv_pspoll(ic, ni, m);
49090634029Sdyoung break;
49190634029Sdyoung }
492b8e804e8Sdyoung }
4931151deddSmaxv #endif
4941151deddSmaxv }
4951151deddSmaxv
4961151deddSmaxv /* -------------------------------------------------------------------------- */
4971151deddSmaxv
4981151deddSmaxv /*
4991151deddSmaxv * Process a received frame. The node associated with the sender
5001151deddSmaxv * should be supplied. If nothing was found in the node table then
5011151deddSmaxv * the caller is assumed to supply a reference to ic_bss instead.
5021151deddSmaxv * The RSSI and a timestamp are also supplied. The RSSI data is used
5031151deddSmaxv * during AP scanning to select a AP to associate with; it can have
5041151deddSmaxv * any units so long as values have consistent units and higher values
5051151deddSmaxv * mean ``better signal''. The receive timestamp is currently not used
5061151deddSmaxv * by the 802.11 layer.
5071151deddSmaxv */
5081151deddSmaxv int
ieee80211_input(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,int rssi,u_int32_t rstamp)5091151deddSmaxv ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
5101151deddSmaxv struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
5111151deddSmaxv {
5121151deddSmaxv #define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
5131151deddSmaxv #define HAS_SEQ(type) ((type & 0x4) == 0)
5141151deddSmaxv struct ifnet *ifp = ic->ic_ifp;
5151151deddSmaxv struct ieee80211_frame *wh;
5161151deddSmaxv u_int8_t dir, type;
5171151deddSmaxv u_int16_t rxseq;
5181151deddSmaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
5191151deddSmaxv int ret;
5201151deddSmaxv
5211151deddSmaxv KASSERT(!cpu_intr_p());
5221151deddSmaxv
5231151deddSmaxv IASSERT(ni != NULL, ("null node"));
5241151deddSmaxv ni->ni_inact = ni->ni_inact_reload;
5251151deddSmaxv
5261151deddSmaxv /* trim CRC here so WEP can find its own CRC at the end of packet. */
5271151deddSmaxv if (m->m_flags & M_HASFCS) {
5281151deddSmaxv m_adj(m, -IEEE80211_CRC_LEN);
5291151deddSmaxv m->m_flags &= ~M_HASFCS;
5301151deddSmaxv }
5311151deddSmaxv type = -1; /* undefined */
5321151deddSmaxv
5331151deddSmaxv /*
5341151deddSmaxv * In monitor mode, send everything directly to bpf.
5351151deddSmaxv * XXX may want to include the CRC
5361151deddSmaxv */
5371151deddSmaxv if (ic->ic_opmode == IEEE80211_M_MONITOR)
538b8e804e8Sdyoung goto out;
5391151deddSmaxv
540a45976c2Smaxv if (m->m_len < sizeof(struct ieee80211_frame_min)) {
5411151deddSmaxv IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
5421151deddSmaxv ni->ni_macaddr, NULL,
543a45976c2Smaxv "too short (1): len %u", m->m_len);
5441151deddSmaxv ic->ic_stats.is_rx_tooshort++;
5451151deddSmaxv goto out;
5461151deddSmaxv }
5471151deddSmaxv
5481151deddSmaxv /*
5491151deddSmaxv * Bit of a cheat here, we use a pointer for a 3-address
5501151deddSmaxv * frame format but don't reference fields past outside
5511151deddSmaxv * ieee80211_frame_min w/o first validating the data is
5521151deddSmaxv * present.
5531151deddSmaxv */
5541151deddSmaxv wh = mtod(m, struct ieee80211_frame *);
5551151deddSmaxv
5561151deddSmaxv if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
5571151deddSmaxv IEEE80211_FC0_VERSION_0) {
5581151deddSmaxv IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
5591151deddSmaxv ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
5601151deddSmaxv ic->ic_stats.is_rx_badversion++;
5611151deddSmaxv goto err;
5621151deddSmaxv }
5631151deddSmaxv
5641151deddSmaxv dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
5651151deddSmaxv type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
5661151deddSmaxv
5671151deddSmaxv if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
5681151deddSmaxv u_int8_t *bssid;
5691151deddSmaxv
5701151deddSmaxv switch (ic->ic_opmode) {
5711151deddSmaxv case IEEE80211_M_STA:
5721151deddSmaxv bssid = wh->i_addr2;
5731151deddSmaxv if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
5741151deddSmaxv /* not interested in */
5751151deddSmaxv IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
5761151deddSmaxv bssid, NULL, "node %s, %s",
5771151deddSmaxv ether_snprintf(ebuf, sizeof(ebuf),
5781151deddSmaxv ni->ni_bssid), "not to bss");
5791151deddSmaxv ic->ic_stats.is_rx_wrongbss++;
5801151deddSmaxv goto out;
5811151deddSmaxv }
5821151deddSmaxv
5831151deddSmaxv /*
5841151deddSmaxv * Filter out packets not directed to us in case the
5851151deddSmaxv * device is in promiscuous mode
5861151deddSmaxv */
5871151deddSmaxv if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
5881151deddSmaxv !IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
5891151deddSmaxv IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
5901151deddSmaxv bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D",
5911151deddSmaxv ic->ic_myaddr, ":", wh->i_addr1, ":");
5921151deddSmaxv ic->ic_stats.is_rx_wrongbss++;
5931151deddSmaxv goto out;
5941151deddSmaxv }
5951151deddSmaxv break;
5961151deddSmaxv
5971151deddSmaxv case IEEE80211_M_IBSS:
5981151deddSmaxv case IEEE80211_M_AHDEMO:
5991151deddSmaxv case IEEE80211_M_HOSTAP:
6001151deddSmaxv if (dir != IEEE80211_FC1_DIR_NODS)
6011151deddSmaxv bssid = wh->i_addr1;
6021151deddSmaxv else if (type == IEEE80211_FC0_TYPE_CTL)
6031151deddSmaxv bssid = wh->i_addr1;
6041151deddSmaxv else {
605a45976c2Smaxv if (m->m_len < sizeof(struct ieee80211_frame)) {
6061151deddSmaxv IEEE80211_DISCARD_MAC(ic,
6071151deddSmaxv IEEE80211_MSG_ANY, ni->ni_macaddr,
6081151deddSmaxv NULL, "too short (2): len %u",
609a45976c2Smaxv m->m_len);
6101151deddSmaxv ic->ic_stats.is_rx_tooshort++;
6111151deddSmaxv goto out;
6121151deddSmaxv }
6131151deddSmaxv bssid = wh->i_addr3;
6141151deddSmaxv }
6151151deddSmaxv if (type != IEEE80211_FC0_TYPE_DATA)
6161151deddSmaxv break;
6171151deddSmaxv
6181151deddSmaxv /*
6191151deddSmaxv * Data frame, validate the bssid.
6201151deddSmaxv */
6211151deddSmaxv if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
6221151deddSmaxv !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
6231151deddSmaxv /* not interested in */
6241151deddSmaxv IEEE80211_DEBUGVAR(
6251151deddSmaxv char bbuf[3 * ETHER_ADDR_LEN]);
6261151deddSmaxv IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
6271151deddSmaxv bssid, NULL, "bss %s, broadcast %s, %s",
6281151deddSmaxv ether_snprintf(ebuf, sizeof(ebuf),
6291151deddSmaxv ic->ic_bss->ni_bssid),
6301151deddSmaxv ether_snprintf(bbuf, sizeof(bbuf),
6311151deddSmaxv ifp->if_broadcastaddr), "not to bss");
6321151deddSmaxv ic->ic_stats.is_rx_wrongbss++;
6331151deddSmaxv goto out;
6341151deddSmaxv }
6351151deddSmaxv
6361151deddSmaxv /*
6371151deddSmaxv * For adhoc mode we cons up a node when it doesn't
6381151deddSmaxv * exist. This should probably be done after an ACL
6391151deddSmaxv * check.
6401151deddSmaxv */
6411151deddSmaxv if (ni == ic->ic_bss &&
6421151deddSmaxv ic->ic_opmode != IEEE80211_M_HOSTAP &&
6431151deddSmaxv !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
6441151deddSmaxv /*
6451151deddSmaxv * Fake up a node for this newly
6461151deddSmaxv * discovered member of the IBSS.
6471151deddSmaxv */
6481151deddSmaxv ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
6491151deddSmaxv wh->i_addr2);
6501151deddSmaxv if (ni == NULL) {
6511151deddSmaxv /* NB: stat kept for alloc failure */
6521151deddSmaxv goto err;
6531151deddSmaxv }
6541151deddSmaxv }
6551151deddSmaxv break;
6561151deddSmaxv
6571151deddSmaxv default:
6581151deddSmaxv goto out;
6591151deddSmaxv }
6601151deddSmaxv
6611151deddSmaxv ni->ni_rssi = rssi;
6621151deddSmaxv ni->ni_rstamp = rstamp;
6631151deddSmaxv
6641151deddSmaxv if (HAS_SEQ(type) && (ic->ic_opmode != IEEE80211_M_STA ||
6651151deddSmaxv !IEEE80211_IS_MULTICAST(wh->i_addr1))) {
6661151deddSmaxv u_int8_t tid, retry;
6671151deddSmaxv u_int16_t rxno, orxno;
6681151deddSmaxv
6691151deddSmaxv if (ieee80211_has_qos(wh)) {
6701151deddSmaxv struct ieee80211_qosframe *qosf;
6711151deddSmaxv
672a45976c2Smaxv if (m->m_len < sizeof(struct ieee80211_qosframe)) {
673a45976c2Smaxv IEEE80211_DISCARD_MAC(ic,
674a45976c2Smaxv IEEE80211_MSG_ANY,
675a45976c2Smaxv ni->ni_macaddr, NULL,
676a45976c2Smaxv "too short (1): len %u", m->m_len);
677a45976c2Smaxv ic->ic_stats.is_rx_tooshort++;
678a45976c2Smaxv goto out;
679a45976c2Smaxv }
6801151deddSmaxv qosf = mtod(m, struct ieee80211_qosframe *);
6811151deddSmaxv
6821151deddSmaxv tid = qosf->i_qos[0] & IEEE80211_QOS_TID;
6831151deddSmaxv if (TID_TO_WME_AC(tid) >= WME_AC_VI)
6841151deddSmaxv ic->ic_wme.wme_hipri_traffic++;
6851151deddSmaxv tid++;
6861151deddSmaxv } else {
6871151deddSmaxv tid = 0;
6881151deddSmaxv }
6891151deddSmaxv
6901151deddSmaxv rxseq = le16toh(*(u_int16_t *)wh->i_seq);
6911151deddSmaxv retry = wh->i_fc[1] & IEEE80211_FC1_RETRY;
6921151deddSmaxv rxno = rxseq >> IEEE80211_SEQ_SEQ_SHIFT;
6931151deddSmaxv orxno = ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT;
6941151deddSmaxv
6951151deddSmaxv if (retry && (
6961151deddSmaxv (orxno == 4095 && rxno == orxno) ||
6971151deddSmaxv (orxno != 4095 &&
6981151deddSmaxv SEQ_LEQ(rxseq, ni->ni_rxseqs[tid]))
6991151deddSmaxv )) {
7001151deddSmaxv /* duplicate, discard */
7011151deddSmaxv IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
7021151deddSmaxv bssid, "duplicate",
7031151deddSmaxv "seqno <%u,%u> fragno <%u,%u> tid %u",
7041151deddSmaxv rxno,
7051151deddSmaxv orxno,
7061151deddSmaxv rxseq & IEEE80211_SEQ_FRAG_MASK,
7071151deddSmaxv ni->ni_rxseqs[tid] &
7081151deddSmaxv IEEE80211_SEQ_FRAG_MASK,
7091151deddSmaxv tid);
7101151deddSmaxv ic->ic_stats.is_rx_dup++;
7111151deddSmaxv IEEE80211_NODE_STAT(ni, rx_dup);
7121151deddSmaxv goto out;
7131151deddSmaxv }
7141151deddSmaxv ni->ni_rxseqs[tid] = rxseq;
7151151deddSmaxv }
7161151deddSmaxv }
7171151deddSmaxv
7181151deddSmaxv switch (type) {
7191151deddSmaxv case IEEE80211_FC0_TYPE_DATA:
7201151deddSmaxv ret = ieee80211_input_data(ic, &m, ni);
7211151deddSmaxv if (ret == -1) {
7221151deddSmaxv goto out;
7231151deddSmaxv }
7241151deddSmaxv return IEEE80211_FC0_TYPE_DATA;
7251151deddSmaxv
7261151deddSmaxv case IEEE80211_FC0_TYPE_MGT:
7271151deddSmaxv ret = ieee80211_input_management(ic, &m, ni, rssi, rstamp);
7281151deddSmaxv if (ret == -1) {
7291151deddSmaxv goto out;
7301151deddSmaxv }
7311151deddSmaxv return IEEE80211_FC0_TYPE_MGT;
7321151deddSmaxv
7331151deddSmaxv case IEEE80211_FC0_TYPE_CTL:
7341151deddSmaxv ieee80211_input_control(ic, m, ni);
7351151deddSmaxv goto out;
7361151deddSmaxv
73740e261aaSdyoung default:
73890634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
73990634029Sdyoung wh, NULL, "bad frame type 0x%x", type);
74040e261aaSdyoung /* should not come here */
74140e261aaSdyoung break;
74240e261aaSdyoung }
7436d2a143aSmaxv
74440e261aaSdyoung err:
7451a2cab17Sthorpej if_statinc(ifp, if_ierrors);
7466d2a143aSmaxv
74740e261aaSdyoung out:
74840e261aaSdyoung if (m != NULL) {
7493cd62456Smsaitoh bpf_mtap3(ic->ic_rawbpf, m, BPF_D_IN);
75040e261aaSdyoung m_freem(m);
75140e261aaSdyoung }
75290634029Sdyoung return type;
75390634029Sdyoung #undef SEQ_LEQ
75440e261aaSdyoung }
75540e261aaSdyoung
75690634029Sdyoung /*
7576d2a143aSmaxv * This function reassembles fragments.
75890634029Sdyoung */
75990634029Sdyoung static struct mbuf *
ieee80211_defrag(struct ieee80211_node * ni,struct mbuf * m,int hdrspace)7601f639d06Smaxv ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
76140e261aaSdyoung {
76290634029Sdyoung struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
76390634029Sdyoung struct ieee80211_frame *lwh;
7640a9aff6fSmaxv u_int16_t rxseq, iseq;
76590634029Sdyoung u_int8_t fragno;
76601f382e3Smaxv const u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
76790634029Sdyoung struct mbuf *mfrag;
76890634029Sdyoung
76990634029Sdyoung IASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
77090634029Sdyoung
7710a9aff6fSmaxv iseq = *(u_int16_t *)wh->i_seq;
7720a9aff6fSmaxv rxseq = le16toh(iseq);
77390634029Sdyoung fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
77490634029Sdyoung
77590634029Sdyoung /* Quick way out, if there's nothing to defragment */
77690634029Sdyoung if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
77790634029Sdyoung return m;
77890634029Sdyoung
77990634029Sdyoung /*
78090634029Sdyoung * Remove frag to insure it doesn't get reaped by timer.
78190634029Sdyoung */
78290634029Sdyoung if (ni->ni_table == NULL) {
78390634029Sdyoung /*
78490634029Sdyoung * Should never happen. If the node is orphaned (not in
78590634029Sdyoung * the table) then input packets should not reach here.
78690634029Sdyoung * Otherwise, a concurrent request that yanks the table
78790634029Sdyoung * should be blocked by other interlocking and/or by first
78890634029Sdyoung * shutting the driver down. Regardless, be defensive
78990634029Sdyoung * here and just bail
79090634029Sdyoung */
79190634029Sdyoung /* XXX need msg+stat */
79290634029Sdyoung m_freem(m);
79390634029Sdyoung return NULL;
79490634029Sdyoung }
79590634029Sdyoung IEEE80211_NODE_LOCK(ni->ni_table);
79690634029Sdyoung mfrag = ni->ni_rxfrag[0];
79790634029Sdyoung ni->ni_rxfrag[0] = NULL;
79890634029Sdyoung IEEE80211_NODE_UNLOCK(ni->ni_table);
79990634029Sdyoung
80090634029Sdyoung /*
80190634029Sdyoung * Validate new fragment is in order and
80290634029Sdyoung * related to the previous ones.
80390634029Sdyoung */
80490634029Sdyoung if (mfrag != NULL) {
80590634029Sdyoung u_int16_t last_rxseq;
80690634029Sdyoung
80790634029Sdyoung lwh = mtod(mfrag, struct ieee80211_frame *);
80890634029Sdyoung last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq);
80990634029Sdyoung /* NB: check seq # and frag together */
81090634029Sdyoung if (rxseq != last_rxseq+1 ||
81190634029Sdyoung !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
81290634029Sdyoung !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
81390634029Sdyoung /*
81490634029Sdyoung * Unrelated fragment or no space for it,
81590634029Sdyoung * clear current fragments.
81690634029Sdyoung */
81790634029Sdyoung m_freem(mfrag);
81890634029Sdyoung mfrag = NULL;
81990634029Sdyoung }
82090634029Sdyoung }
82190634029Sdyoung
82290634029Sdyoung if (mfrag == NULL) {
82390634029Sdyoung if (fragno != 0) { /* !first fragment, discard */
82490634029Sdyoung IEEE80211_NODE_STAT(ni, rx_defrag);
82590634029Sdyoung m_freem(m);
82690634029Sdyoung return NULL;
82790634029Sdyoung }
82890634029Sdyoung mfrag = m;
82901f382e3Smaxv } else {
8300a9aff6fSmaxv int mlen;
8310a9aff6fSmaxv
83201f382e3Smaxv /* Strip header and concatenate */
83301f382e3Smaxv m_adj(m, hdrspace);
8340a9aff6fSmaxv mlen = m->m_pkthdr.len;
83590634029Sdyoung m_cat(mfrag, m);
83601f382e3Smaxv
83790634029Sdyoung /* NB: m_cat doesn't update the packet header */
8380a9aff6fSmaxv mfrag->m_pkthdr.len += mlen;
83901f382e3Smaxv
84090634029Sdyoung /* track last seqnum and fragno */
84190634029Sdyoung lwh = mtod(mfrag, struct ieee80211_frame *);
8420a9aff6fSmaxv *(u_int16_t *)lwh->i_seq = iseq;
84390634029Sdyoung }
84401f382e3Smaxv
84501f382e3Smaxv if (more_frag) {
84601f382e3Smaxv /* more to come, save */
84790634029Sdyoung ni->ni_rxfragstamp = ticks;
84890634029Sdyoung ni->ni_rxfrag[0] = mfrag;
84990634029Sdyoung mfrag = NULL;
85090634029Sdyoung }
85101f382e3Smaxv
85290634029Sdyoung return mfrag;
85390634029Sdyoung }
85490634029Sdyoung
85587515e34Sskrll static void
ieee80211_deliver_data(struct ieee80211com * ic,struct ieee80211_node * ni,struct mbuf * m)85687515e34Sskrll ieee80211_deliver_data(struct ieee80211com *ic,
85787515e34Sskrll struct ieee80211_node *ni, struct mbuf *m)
85887515e34Sskrll {
85987515e34Sskrll struct ether_header *eh = mtod(m, struct ether_header *);
86087515e34Sskrll struct ifnet *ifp = ic->ic_ifp;
86121268b39Sjoerg int error;
86287515e34Sskrll
86387515e34Sskrll /* perform as a bridge within the AP */
86487515e34Sskrll if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
86587515e34Sskrll (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
86687515e34Sskrll struct mbuf *m1 = NULL;
86787515e34Sskrll
86887515e34Sskrll if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
86987515e34Sskrll m1 = m_copypacket(m, M_DONTWAIT);
87087515e34Sskrll if (m1 == NULL)
8711a2cab17Sthorpej if_statinc(ifp, if_oerrors);
87287515e34Sskrll else
87387515e34Sskrll m1->m_flags |= M_MCAST;
87487515e34Sskrll } else {
87587515e34Sskrll /*
87687515e34Sskrll * Check if the destination is known; if so
87787515e34Sskrll * and the port is authorized dispatch directly.
87887515e34Sskrll */
87987515e34Sskrll struct ieee80211_node *sta =
88087515e34Sskrll ieee80211_find_node(&ic->ic_sta, eh->ether_dhost);
88187515e34Sskrll if (sta != NULL) {
88287515e34Sskrll if (ieee80211_node_is_authorized(sta)) {
88387515e34Sskrll /*
88487515e34Sskrll * Beware of sending to ourself; this
88587515e34Sskrll * needs to happen via the normal
88687515e34Sskrll * input path.
88787515e34Sskrll */
88887515e34Sskrll if (sta != ic->ic_bss) {
88987515e34Sskrll m1 = m;
89087515e34Sskrll m = NULL;
89187515e34Sskrll }
89287515e34Sskrll } else {
89387515e34Sskrll ic->ic_stats.is_rx_unauth++;
89487515e34Sskrll IEEE80211_NODE_STAT(sta, rx_unauth);
89587515e34Sskrll }
89687515e34Sskrll ieee80211_free_node(sta);
89787515e34Sskrll }
89887515e34Sskrll }
899bea24f0eSmaxv
90087515e34Sskrll if (m1 != NULL) {
90187515e34Sskrll int len;
90287515e34Sskrll #ifdef ALTQ
90387515e34Sskrll if (ALTQ_IS_ENABLED(&ifp->if_snd)) {
90440b1061cSknakahara altq_etherclassify(&ifp->if_snd, m1);
90587515e34Sskrll }
90687515e34Sskrll #endif
90787515e34Sskrll len = m1->m_pkthdr.len;
908b76ec0b0Sknakahara IFQ_ENQUEUE(&ifp->if_snd, m1, error);
90921268b39Sjoerg if (error) {
9101a2cab17Sthorpej if_statinc(ifp, if_oerrors);
911bea24f0eSmaxv m_freem(m);
91221268b39Sjoerg m = NULL;
91321268b39Sjoerg }
9141a2cab17Sthorpej if_statadd(ifp, if_obytes, len);
91587515e34Sskrll }
91687515e34Sskrll }
91787515e34Sskrll
918bea24f0eSmaxv if (m != NULL) {
919a6e88d78Sknakahara if (ni->ni_vlan != 0)
920a6e88d78Sknakahara vlan_set_tag(m, ni->ni_vlan);
9219c4cd063Sozaki-r
9229c4cd063Sozaki-r /*
9239c4cd063Sozaki-r * XXX once ieee80211_input (or rxintr itself) runs in softint
9249c4cd063Sozaki-r * we have to change here too to use if_input.
9259c4cd063Sozaki-r */
9269c4cd063Sozaki-r KASSERT(ifp->if_percpuq);
9279c4cd063Sozaki-r if_percpuq_enqueue(ifp->if_percpuq, m);
92887515e34Sskrll }
929bea24f0eSmaxv
93087515e34Sskrll return;
93187515e34Sskrll }
93287515e34Sskrll
93390634029Sdyoung static struct mbuf *
ieee80211_decap(struct mbuf * m,int hdrlen)9341f639d06Smaxv ieee80211_decap(struct mbuf *m, int hdrlen)
93590634029Sdyoung {
93680b91eeaSdyoung struct ieee80211_qosframe_addr4 wh; /* Max size address frames */
93740e261aaSdyoung struct ether_header *eh;
93840e261aaSdyoung struct llc *llc;
93940e261aaSdyoung
94080b91eeaSdyoung if (m->m_len < hdrlen + sizeof(*llc) &&
94180b91eeaSdyoung (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
94240e261aaSdyoung return NULL;
94340e261aaSdyoung }
94401f382e3Smaxv
94553524e44Schristos memcpy(&wh, mtod(m, void *), hdrlen);
94601f382e3Smaxv
94753524e44Schristos llc = (struct llc *)(mtod(m, char *) + hdrlen);
94801f382e3Smaxv if (llc->llc_dsap == LLC_SNAP_LSAP &&
94901f382e3Smaxv llc->llc_ssap == LLC_SNAP_LSAP &&
95001f382e3Smaxv llc->llc_control == LLC_UI &&
95101f382e3Smaxv llc->llc_snap.org_code[0] == 0 &&
95201f382e3Smaxv llc->llc_snap.org_code[1] == 0 &&
95301f382e3Smaxv llc->llc_snap.org_code[2] == 0) {
95480b91eeaSdyoung m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
95540e261aaSdyoung llc = NULL;
95640e261aaSdyoung } else {
95701f382e3Smaxv /* Keep the LLC after the Ethernet header. */
95880b91eeaSdyoung m_adj(m, hdrlen - sizeof(*eh));
95940e261aaSdyoung }
96001f382e3Smaxv
96140e261aaSdyoung eh = mtod(m, struct ether_header *);
96201f382e3Smaxv
96340e261aaSdyoung switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
96440e261aaSdyoung case IEEE80211_FC1_DIR_NODS:
96540e261aaSdyoung IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
96640e261aaSdyoung IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
96740e261aaSdyoung break;
96840e261aaSdyoung case IEEE80211_FC1_DIR_TODS:
96940e261aaSdyoung IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
97040e261aaSdyoung IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
97140e261aaSdyoung break;
97240e261aaSdyoung case IEEE80211_FC1_DIR_FROMDS:
97340e261aaSdyoung IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
97440e261aaSdyoung IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
97540e261aaSdyoung break;
97640e261aaSdyoung case IEEE80211_FC1_DIR_DSTODS:
97780b91eeaSdyoung IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
97880b91eeaSdyoung IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
97980b91eeaSdyoung break;
98040e261aaSdyoung }
98101f382e3Smaxv
98240e261aaSdyoung #ifdef ALIGNED_POINTER
983fffc9c66Schristos if (!ALIGNED_POINTER(mtod(m, char *) + sizeof(*eh), u_int32_t)) {
98440e261aaSdyoung struct mbuf *n, *n0, **np;
98553524e44Schristos char *newdata;
98640e261aaSdyoung int off, pktlen;
98740e261aaSdyoung
98840e261aaSdyoung n0 = NULL;
98940e261aaSdyoung np = &n0;
99040e261aaSdyoung off = 0;
99140e261aaSdyoung pktlen = m->m_pkthdr.len;
99240e261aaSdyoung while (pktlen > off) {
99340e261aaSdyoung if (n0 == NULL) {
99440e261aaSdyoung MGETHDR(n, M_DONTWAIT, MT_DATA);
99540e261aaSdyoung if (n == NULL) {
99640e261aaSdyoung m_freem(m);
99740e261aaSdyoung return NULL;
99840e261aaSdyoung }
999b1305a6dSmaxv m_move_pkthdr(n, m);
100040e261aaSdyoung n->m_len = MHLEN;
100140e261aaSdyoung } else {
100240e261aaSdyoung MGET(n, M_DONTWAIT, MT_DATA);
100340e261aaSdyoung if (n == NULL) {
100440e261aaSdyoung m_freem(m);
100540e261aaSdyoung m_freem(n0);
100640e261aaSdyoung return NULL;
100740e261aaSdyoung }
100840e261aaSdyoung n->m_len = MLEN;
100940e261aaSdyoung }
101040e261aaSdyoung if (pktlen - off >= MINCLSIZE) {
101140e261aaSdyoung MCLGET(n, M_DONTWAIT);
101240e261aaSdyoung if (n->m_flags & M_EXT)
101340e261aaSdyoung n->m_len = n->m_ext.ext_size;
101440e261aaSdyoung }
101540e261aaSdyoung if (n0 == NULL) {
101640e261aaSdyoung newdata =
101753524e44Schristos (char *)ALIGN(n->m_data + sizeof(*eh)) -
101840e261aaSdyoung sizeof(*eh);
101940e261aaSdyoung n->m_len -= newdata - n->m_data;
102040e261aaSdyoung n->m_data = newdata;
102140e261aaSdyoung }
102240e261aaSdyoung if (n->m_len > pktlen - off)
102340e261aaSdyoung n->m_len = pktlen - off;
102453524e44Schristos m_copydata(m, off, n->m_len, mtod(n, void *));
102540e261aaSdyoung off += n->m_len;
102640e261aaSdyoung *np = n;
102740e261aaSdyoung np = &n->m_next;
102840e261aaSdyoung }
102940e261aaSdyoung m_freem(m);
103040e261aaSdyoung m = n0;
103140e261aaSdyoung }
103240e261aaSdyoung #endif /* ALIGNED_POINTER */
103301f382e3Smaxv
103440e261aaSdyoung if (llc != NULL) {
103540e261aaSdyoung eh = mtod(m, struct ether_header *);
103640e261aaSdyoung eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
103740e261aaSdyoung }
103801f382e3Smaxv
103940e261aaSdyoung return m;
104040e261aaSdyoung }
104140e261aaSdyoung
104240e261aaSdyoung /*
104340e261aaSdyoung * Install received rate set information in the node's state block.
104440e261aaSdyoung */
104587515e34Sskrll int
ieee80211_setup_rates(struct ieee80211_node * ni,const u_int8_t * rates,const u_int8_t * xrates,int flags)104611a42b5cSmaxv ieee80211_setup_rates(struct ieee80211_node *ni, const u_int8_t *rates,
104711a42b5cSmaxv const u_int8_t *xrates, int flags)
104840e261aaSdyoung {
104987515e34Sskrll struct ieee80211com *ic = ni->ni_ic;
105040e261aaSdyoung struct ieee80211_rateset *rs = &ni->ni_rates;
105140e261aaSdyoung
105240e261aaSdyoung memset(rs, 0, sizeof(*rs));
105311a42b5cSmaxv
105440e261aaSdyoung rs->rs_nrates = rates[1];
105540e261aaSdyoung memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
105611a42b5cSmaxv
105740e261aaSdyoung if (xrates != NULL) {
105840e261aaSdyoung u_int8_t nxrates;
105911a42b5cSmaxv size_t totalrate;
106011a42b5cSmaxv
106140e261aaSdyoung /*
106240e261aaSdyoung * Tack on 11g extended supported rate element.
106340e261aaSdyoung */
106440e261aaSdyoung nxrates = xrates[1];
106511a42b5cSmaxv totalrate = (size_t)rs->rs_nrates + (size_t)nxrates;
106611a42b5cSmaxv
106711a42b5cSmaxv if (totalrate > IEEE80211_RATE_MAXSIZE) {
1068c2ec5838Schristos IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
106940e261aaSdyoung nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
1070f0546001Smycroft IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
107190634029Sdyoung "[%s] extended rate set too large;"
107240e261aaSdyoung " only using %u of %u rates\n",
1073c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
1074c2ec5838Schristos nxrates, xrates[1]);
10759280f4b4Sdyoung ic->ic_stats.is_rx_rstoobig++;
107640e261aaSdyoung }
107711a42b5cSmaxv
107840e261aaSdyoung memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
107940e261aaSdyoung rs->rs_nrates += nxrates;
108040e261aaSdyoung }
108111a42b5cSmaxv
108287515e34Sskrll return ieee80211_fix_rate(ni, flags);
108340e261aaSdyoung }
108440e261aaSdyoung
1085db0e43b2Sdyoung static void
ieee80211_auth_open(struct ieee80211com * ic,struct ieee80211_frame * wh,struct ieee80211_node * ni,int rssi,u_int32_t rstamp,u_int16_t seq,u_int16_t status)1086db0e43b2Sdyoung ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
1087168cd830Schristos struct ieee80211_node *ni, int rssi, u_int32_t rstamp,
10884d595fd7Schristos u_int16_t seq, u_int16_t status)
1089db0e43b2Sdyoung {
1090c2ec5838Schristos IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
109190634029Sdyoung
109280b91eeaSdyoung if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
109380b91eeaSdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
109480b91eeaSdyoung ni->ni_macaddr, "open auth",
109580b91eeaSdyoung "bad sta auth mode %u", ni->ni_authmode);
109680b91eeaSdyoung ic->ic_stats.is_rx_bad_auth++; /* XXX */
1097a45976c2Smaxv
109880b91eeaSdyoung if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
109980b91eeaSdyoung /* XXX hack to workaround calling convention */
110080b91eeaSdyoung ieee80211_send_error(ic, ni, wh->i_addr2,
110180b91eeaSdyoung IEEE80211_FC0_SUBTYPE_AUTH,
110280b91eeaSdyoung (seq + 1) | (IEEE80211_STATUS_ALG<<16));
110380b91eeaSdyoung }
1104db0e43b2Sdyoung return;
11059280f4b4Sdyoung }
1106a45976c2Smaxv
110780b91eeaSdyoung switch (ic->ic_opmode) {
110880b91eeaSdyoung case IEEE80211_M_IBSS:
1109db0e43b2Sdyoung case IEEE80211_M_AHDEMO:
111080b91eeaSdyoung case IEEE80211_M_MONITOR:
1111db0e43b2Sdyoung /* should not come here */
111280b91eeaSdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
111380b91eeaSdyoung ni->ni_macaddr, "open auth",
111480b91eeaSdyoung "bad operating mode %u", ic->ic_opmode);
1115db0e43b2Sdyoung break;
1116db0e43b2Sdyoung
1117db0e43b2Sdyoung case IEEE80211_M_HOSTAP:
111874988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
11193ebaf267Smycroft if (ic->ic_state != IEEE80211_S_RUN ||
11209280f4b4Sdyoung seq != IEEE80211_AUTH_OPEN_REQUEST) {
11219280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
1122db0e43b2Sdyoung return;
11239280f4b4Sdyoung }
1124a45976c2Smaxv
112590634029Sdyoung /* always accept open authentication requests */
1126db0e43b2Sdyoung if (ni == ic->ic_bss) {
112790634029Sdyoung ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
112890634029Sdyoung if (ni == NULL)
1129db0e43b2Sdyoung return;
1130a45976c2Smaxv } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) {
113190634029Sdyoung (void)ieee80211_ref_node(ni);
1132a45976c2Smaxv }
1133a45976c2Smaxv
113480b91eeaSdyoung /*
1135f0a7346dSsnj * Mark the node as referenced to reflect that its
113680b91eeaSdyoung * reference count has been bumped to insure it remains
113780b91eeaSdyoung * after the transaction completes.
113880b91eeaSdyoung */
113980b91eeaSdyoung ni->ni_flags |= IEEE80211_NODE_AREF;
114080b91eeaSdyoung
1141a45976c2Smaxv IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH,
1142a45976c2Smaxv seq + 1);
1143a02b46e8Smycroft IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
114490634029Sdyoung "[%s] station authenticated (open)\n",
1145c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
1146a45976c2Smaxv
114780b91eeaSdyoung /*
114880b91eeaSdyoung * When 802.1x is not in use mark the port
114980b91eeaSdyoung * authorized at this point so traffic can flow.
115080b91eeaSdyoung */
115180b91eeaSdyoung if (ni->ni_authmode != IEEE80211_AUTH_8021X)
115287515e34Sskrll ieee80211_node_authorize(ni);
115374988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
1154db0e43b2Sdyoung break;
1155a02b46e8Smycroft
1156db0e43b2Sdyoung case IEEE80211_M_STA:
1157db0e43b2Sdyoung if (ic->ic_state != IEEE80211_S_AUTH ||
11589280f4b4Sdyoung seq != IEEE80211_AUTH_OPEN_RESPONSE) {
11599280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
1160db0e43b2Sdyoung return;
11619280f4b4Sdyoung }
1162db0e43b2Sdyoung if (status != 0) {
1163a02b46e8Smycroft IEEE80211_DPRINTF(ic,
1164a02b46e8Smycroft IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
116590634029Sdyoung "[%s] open auth failed (reason %d)\n",
1166c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
1167c2ec5838Schristos status);
1168a45976c2Smaxv
116990634029Sdyoung /* XXX can this happen? */
1170db0e43b2Sdyoung if (ni != ic->ic_bss)
1171db0e43b2Sdyoung ni->ni_fails++;
1172a45976c2Smaxv
11739280f4b4Sdyoung ic->ic_stats.is_rx_auth_fail++;
117480b91eeaSdyoung ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
1175a45976c2Smaxv } else {
1176db0e43b2Sdyoung ieee80211_new_state(ic, IEEE80211_S_ASSOC,
1177db0e43b2Sdyoung wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
1178a45976c2Smaxv }
1179db0e43b2Sdyoung break;
1180db0e43b2Sdyoung }
1181db0e43b2Sdyoung }
1182db0e43b2Sdyoung
118380b91eeaSdyoung /*
118480b91eeaSdyoung * Send a management frame error response to the specified
118580b91eeaSdyoung * station. If ni is associated with the station then use
118680b91eeaSdyoung * it; otherwise allocate a temporary node suitable for
118780b91eeaSdyoung * transmitting the frame and then free the reference so
118880b91eeaSdyoung * it will go away as soon as the frame has been transmitted.
118980b91eeaSdyoung */
119080b91eeaSdyoung static void
ieee80211_send_error(struct ieee80211com * ic,struct ieee80211_node * ni,const u_int8_t * mac,int subtype,int arg)119180b91eeaSdyoung ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
119280b91eeaSdyoung const u_int8_t *mac, int subtype, int arg)
119380b91eeaSdyoung {
1194a45976c2Smaxv bool istmp;
119580b91eeaSdyoung
119680b91eeaSdyoung if (ni == ic->ic_bss) {
119787515e34Sskrll ni = ieee80211_tmp_node(ic, mac);
119880b91eeaSdyoung if (ni == NULL) {
119980b91eeaSdyoung /* XXX msg */
120080b91eeaSdyoung return;
120180b91eeaSdyoung }
1202a45976c2Smaxv istmp = true;
1203a45976c2Smaxv } else {
1204a45976c2Smaxv istmp = false;
1205a45976c2Smaxv }
1206a45976c2Smaxv
120780b91eeaSdyoung IEEE80211_SEND_MGMT(ic, ni, subtype, arg);
120880b91eeaSdyoung if (istmp)
120980b91eeaSdyoung ieee80211_free_node(ni);
121080b91eeaSdyoung }
121180b91eeaSdyoung
121290634029Sdyoung static int
alloc_challenge(struct ieee80211com * ic,struct ieee80211_node * ni)121390634029Sdyoung alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
121490634029Sdyoung {
1215a45976c2Smaxv if (ni->ni_challenge == NULL) {
12169b87d582Scegger ni->ni_challenge = malloc(IEEE80211_CHALLENGE_LEN,
121790634029Sdyoung M_DEVBUF, M_NOWAIT);
1218a45976c2Smaxv }
121990634029Sdyoung if (ni->ni_challenge == NULL) {
1220c2ec5838Schristos IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
1221c2ec5838Schristos
122290634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
122390634029Sdyoung "[%s] shared key challenge alloc failed\n",
1224c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
122590634029Sdyoung /* XXX statistic */
122690634029Sdyoung }
122790634029Sdyoung return (ni->ni_challenge != NULL);
122890634029Sdyoung }
122990634029Sdyoung
123090634029Sdyoung /* XXX TODO: add statistics */
1231db0e43b2Sdyoung static void
ieee80211_auth_shared(struct ieee80211com * ic,struct ieee80211_frame * wh,u_int8_t * frm,u_int8_t * efrm,struct ieee80211_node * ni,int rssi,u_int32_t rstamp,u_int16_t seq,u_int16_t status)1232db0e43b2Sdyoung ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
1233db0e43b2Sdyoung u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi,
1234db0e43b2Sdyoung u_int32_t rstamp, u_int16_t seq, u_int16_t status)
1235db0e43b2Sdyoung {
123690634029Sdyoung u_int8_t *challenge;
123774988b0fSdyoung int estatus;
1238c2ec5838Schristos IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
1239db0e43b2Sdyoung
124090634029Sdyoung /*
124190634029Sdyoung * NB: this can happen as we allow pre-shared key
124290634029Sdyoung * authentication to be enabled w/o wep being turned
124390634029Sdyoung * on so that configuration of these can be done
124490634029Sdyoung * in any order. It may be better to enforce the
124590634029Sdyoung * ordering in which case this check would just be
124690634029Sdyoung * for sanity/consistency.
124790634029Sdyoung */
124850d44f4fSmycroft if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
124990634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
125090634029Sdyoung ni->ni_macaddr, "shared key auth",
125190634029Sdyoung "%s", " PRIVACY is disabled");
125290634029Sdyoung estatus = IEEE80211_STATUS_ALG;
125390634029Sdyoung goto bad;
125490634029Sdyoung }
1255a45976c2Smaxv
125690634029Sdyoung /*
125790634029Sdyoung * Pre-shared key authentication is evil; accept
125890634029Sdyoung * it only if explicitly configured (it is supported
125990634029Sdyoung * mainly for compatibility with clients like OS X).
126090634029Sdyoung */
126190634029Sdyoung if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
126290634029Sdyoung ni->ni_authmode != IEEE80211_AUTH_SHARED) {
126390634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
126490634029Sdyoung ni->ni_macaddr, "shared key auth",
126590634029Sdyoung "bad sta auth mode %u", ni->ni_authmode);
126690634029Sdyoung ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
126790634029Sdyoung estatus = IEEE80211_STATUS_ALG;
126890634029Sdyoung goto bad;
1269db0e43b2Sdyoung }
1270db0e43b2Sdyoung
127190634029Sdyoung challenge = NULL;
1272db0e43b2Sdyoung if (frm + 1 < efrm) {
127390634029Sdyoung if ((frm[1] + 2) > (efrm - frm)) {
127490634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
127590634029Sdyoung ni->ni_macaddr, "shared key auth",
127690634029Sdyoung "ie %d/%d too long",
127790634029Sdyoung frm[0], (frm[1] + 2) - (efrm - frm));
12789280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
127990634029Sdyoung estatus = IEEE80211_STATUS_CHALLENGE;
128090634029Sdyoung goto bad;
1281db0e43b2Sdyoung }
1282db0e43b2Sdyoung if (*frm == IEEE80211_ELEMID_CHALLENGE)
1283db0e43b2Sdyoung challenge = frm;
1284db0e43b2Sdyoung frm += frm[1] + 2;
1285db0e43b2Sdyoung }
1286a45976c2Smaxv
1287db0e43b2Sdyoung switch (seq) {
1288db0e43b2Sdyoung case IEEE80211_AUTH_SHARED_CHALLENGE:
1289db0e43b2Sdyoung case IEEE80211_AUTH_SHARED_RESPONSE:
1290db0e43b2Sdyoung if (challenge == NULL) {
129190634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
129290634029Sdyoung ni->ni_macaddr, "shared key auth",
129390634029Sdyoung "%s", "no challenge");
12949280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
129590634029Sdyoung estatus = IEEE80211_STATUS_CHALLENGE;
129690634029Sdyoung goto bad;
1297db0e43b2Sdyoung }
1298db0e43b2Sdyoung if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
129990634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
130090634029Sdyoung ni->ni_macaddr, "shared key auth",
130190634029Sdyoung "bad challenge len %d", challenge[1]);
13029280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
130390634029Sdyoung estatus = IEEE80211_STATUS_CHALLENGE;
130490634029Sdyoung goto bad;
1305db0e43b2Sdyoung }
1306db0e43b2Sdyoung default:
1307db0e43b2Sdyoung break;
1308db0e43b2Sdyoung }
1309a45976c2Smaxv
1310db0e43b2Sdyoung switch (ic->ic_opmode) {
1311db0e43b2Sdyoung case IEEE80211_M_MONITOR:
1312db0e43b2Sdyoung case IEEE80211_M_AHDEMO:
1313db0e43b2Sdyoung case IEEE80211_M_IBSS:
131490634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
131590634029Sdyoung ni->ni_macaddr, "shared key auth",
131690634029Sdyoung "bad operating mode %u", ic->ic_opmode);
1317db0e43b2Sdyoung return;
1318a45976c2Smaxv
1319db0e43b2Sdyoung case IEEE80211_M_HOSTAP:
132074988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
132174988b0fSdyoung {
132274988b0fSdyoung int allocbs;
1323db0e43b2Sdyoung if (ic->ic_state != IEEE80211_S_RUN) {
132490634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
132590634029Sdyoung ni->ni_macaddr, "shared key auth",
132690634029Sdyoung "bad state %u", ic->ic_state);
132790634029Sdyoung estatus = IEEE80211_STATUS_ALG; /* XXX */
132890634029Sdyoung goto bad;
1329db0e43b2Sdyoung }
1330db0e43b2Sdyoung switch (seq) {
1331db0e43b2Sdyoung case IEEE80211_AUTH_SHARED_REQUEST:
1332db0e43b2Sdyoung if (ni == ic->ic_bss) {
133390634029Sdyoung ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
13349280f4b4Sdyoung if (ni == NULL) {
133590634029Sdyoung /* NB: no way to return an error */
1336db0e43b2Sdyoung return;
13379280f4b4Sdyoung }
133890634029Sdyoung allocbs = 1;
133990634029Sdyoung } else {
134080b91eeaSdyoung if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
134190634029Sdyoung (void)ieee80211_ref_node(ni);
134290634029Sdyoung allocbs = 0;
134390634029Sdyoung }
1344c2ec5838Schristos __USE(allocbs);
1345a45976c2Smaxv
134680b91eeaSdyoung /*
1347f0a7346dSsnj * Mark the node as referenced to reflect that its
134880b91eeaSdyoung * reference count has been bumped to insure it remains
134980b91eeaSdyoung * after the transaction completes.
135080b91eeaSdyoung */
135180b91eeaSdyoung ni->ni_flags |= IEEE80211_NODE_AREF;
1352db0e43b2Sdyoung ni->ni_rssi = rssi;
1353db0e43b2Sdyoung ni->ni_rstamp = rstamp;
135490634029Sdyoung if (!alloc_challenge(ic, ni)) {
135590634029Sdyoung /* NB: don't return error so they rexmit */
1356db0e43b2Sdyoung return;
1357db0e43b2Sdyoung }
1358a45976c2Smaxv
135990634029Sdyoung get_random_bytes(ni->ni_challenge,
136090634029Sdyoung IEEE80211_CHALLENGE_LEN);
1361a45976c2Smaxv
1362f0546001Smycroft IEEE80211_DPRINTF(ic,
1363f0546001Smycroft IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
136490634029Sdyoung "[%s] shared key %sauth request\n",
1365c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf),
1366c2ec5838Schristos ni->ni_macaddr),
136790634029Sdyoung allocbs ? "" : "re");
1368db0e43b2Sdyoung break;
1369a45976c2Smaxv
1370db0e43b2Sdyoung case IEEE80211_AUTH_SHARED_RESPONSE:
1371db0e43b2Sdyoung if (ni == ic->ic_bss) {
137290634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
137390634029Sdyoung ni->ni_macaddr, "shared key response",
137490634029Sdyoung "%s", "unknown station");
137590634029Sdyoung /* NB: don't send a response */
1376db0e43b2Sdyoung return;
1377db0e43b2Sdyoung }
1378a45976c2Smaxv
1379db0e43b2Sdyoung if (ni->ni_challenge == NULL) {
138090634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
138190634029Sdyoung ni->ni_macaddr, "shared key response",
138290634029Sdyoung "%s", "no challenge recorded");
13839280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
138490634029Sdyoung estatus = IEEE80211_STATUS_CHALLENGE;
138590634029Sdyoung goto bad;
1386db0e43b2Sdyoung }
1387a45976c2Smaxv
1388db0e43b2Sdyoung if (memcmp(ni->ni_challenge, &challenge[2],
1389db0e43b2Sdyoung challenge[1]) != 0) {
139090634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
139190634029Sdyoung ni->ni_macaddr, "shared key response",
139290634029Sdyoung "%s", "challenge mismatch");
13939280f4b4Sdyoung ic->ic_stats.is_rx_auth_fail++;
139490634029Sdyoung estatus = IEEE80211_STATUS_CHALLENGE;
139590634029Sdyoung goto bad;
1396db0e43b2Sdyoung }
1397a45976c2Smaxv
1398f0546001Smycroft IEEE80211_DPRINTF(ic,
1399f0546001Smycroft IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
140090634029Sdyoung "[%s] station authenticated (shared key)\n",
1401c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
1402a45976c2Smaxv
140387515e34Sskrll ieee80211_node_authorize(ni);
1404db0e43b2Sdyoung break;
1405a45976c2Smaxv
1406db0e43b2Sdyoung default:
140790634029Sdyoung IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
140890634029Sdyoung ni->ni_macaddr, "shared key auth",
140990634029Sdyoung "bad seq %d", seq);
14109280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
141190634029Sdyoung estatus = IEEE80211_STATUS_SEQUENCE;
141290634029Sdyoung goto bad;
1413db0e43b2Sdyoung }
1414a45976c2Smaxv
1415a45976c2Smaxv IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH,
1416a45976c2Smaxv seq + 1);
141774988b0fSdyoung }
141874988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
1419db0e43b2Sdyoung break;
1420db0e43b2Sdyoung
1421db0e43b2Sdyoung case IEEE80211_M_STA:
1422db0e43b2Sdyoung if (ic->ic_state != IEEE80211_S_AUTH)
1423db0e43b2Sdyoung return;
1424db0e43b2Sdyoung switch (seq) {
1425db0e43b2Sdyoung case IEEE80211_AUTH_SHARED_PASS:
1426db0e43b2Sdyoung if (ni->ni_challenge != NULL) {
14279b87d582Scegger free(ni->ni_challenge, M_DEVBUF);
1428db0e43b2Sdyoung ni->ni_challenge = NULL;
1429db0e43b2Sdyoung }
1430db0e43b2Sdyoung if (status != 0) {
1431f0546001Smycroft IEEE80211_DPRINTF(ic,
1432f0546001Smycroft IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
143390634029Sdyoung "[%s] shared key auth failed (reason %d)\n",
1434c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf),
1435c2ec5838Schristos ieee80211_getbssid(ic, wh)),
143690634029Sdyoung status);
1437a45976c2Smaxv
143890634029Sdyoung /* XXX can this happen? */
1439db0e43b2Sdyoung if (ni != ic->ic_bss)
1440db0e43b2Sdyoung ni->ni_fails++;
1441a45976c2Smaxv
14429280f4b4Sdyoung ic->ic_stats.is_rx_auth_fail++;
1443db0e43b2Sdyoung return;
1444db0e43b2Sdyoung }
1445a45976c2Smaxv
1446db0e43b2Sdyoung ieee80211_new_state(ic, IEEE80211_S_ASSOC,
1447db0e43b2Sdyoung wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
1448db0e43b2Sdyoung break;
1449a45976c2Smaxv
1450db0e43b2Sdyoung case IEEE80211_AUTH_SHARED_CHALLENGE:
145190634029Sdyoung if (!alloc_challenge(ic, ni))
1452db0e43b2Sdyoung return;
145390634029Sdyoung /* XXX could optimize by passing recvd challenge */
1454db0e43b2Sdyoung memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
1455a45976c2Smaxv
1456a45976c2Smaxv IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH,
1457a45976c2Smaxv seq + 1);
1458db0e43b2Sdyoung break;
1459a45976c2Smaxv
1460db0e43b2Sdyoung default:
146190634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH,
146290634029Sdyoung wh, "shared key auth", "bad seq %d", seq);
14639280f4b4Sdyoung ic->ic_stats.is_rx_bad_auth++;
1464db0e43b2Sdyoung return;
1465db0e43b2Sdyoung }
1466db0e43b2Sdyoung break;
1467db0e43b2Sdyoung }
146890634029Sdyoung return;
1469a45976c2Smaxv
147090634029Sdyoung bad:
147174988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
147290634029Sdyoung /*
147390634029Sdyoung * Send an error response; but only when operating as an AP.
147490634029Sdyoung */
147590634029Sdyoung if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
147690634029Sdyoung /* XXX hack to workaround calling convention */
147780b91eeaSdyoung ieee80211_send_error(ic, ni, wh->i_addr2,
147890634029Sdyoung IEEE80211_FC0_SUBTYPE_AUTH,
147990634029Sdyoung (seq + 1) | (estatus<<16));
148080b91eeaSdyoung } else if (ic->ic_opmode == IEEE80211_M_STA) {
148180b91eeaSdyoung /*
148280b91eeaSdyoung * Kick the state machine. This short-circuits
148380b91eeaSdyoung * using the mgt frame timeout to trigger the
148480b91eeaSdyoung * state transition.
148580b91eeaSdyoung */
148680b91eeaSdyoung if (ic->ic_state == IEEE80211_S_AUTH)
148780b91eeaSdyoung ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
1488db0e43b2Sdyoung }
148974988b0fSdyoung #else
149074988b0fSdyoung ;
149174988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
149290634029Sdyoung }
149390634029Sdyoung
149490634029Sdyoung #ifdef IEEE80211_DEBUG
149590634029Sdyoung static void
ieee80211_ssid_mismatch(struct ieee80211com * ic,const char * tag,u_int8_t mac[IEEE80211_ADDR_LEN],u_int8_t * ssid)1496168cd830Schristos ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
149790634029Sdyoung u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid)
149890634029Sdyoung {
1499c2ec5838Schristos char ebuf[3 * ETHER_ADDR_LEN];
1500c2ec5838Schristos
150190634029Sdyoung printf("[%s] discard %s frame, ssid mismatch: ",
1502c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), mac), tag);
150390634029Sdyoung ieee80211_print_essid(ssid + 2, ssid[1]);
150490634029Sdyoung printf("\n");
150590634029Sdyoung }
150690634029Sdyoung
150790634029Sdyoung #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
150890634029Sdyoung if ((_ssid)[1] != 0 && \
150990634029Sdyoung ((_ssid)[1] != (_ni)->ni_esslen || \
151090634029Sdyoung memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
151190634029Sdyoung if (ieee80211_msg_input(ic)) \
151290634029Sdyoung ieee80211_ssid_mismatch(ic, \
151390634029Sdyoung ieee80211_mgt_subtype_name[subtype >> \
151490634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT], \
151590634029Sdyoung wh->i_addr2, _ssid); \
151690634029Sdyoung ic->ic_stats.is_rx_ssidmismatch++; \
151790634029Sdyoung return; \
151890634029Sdyoung } \
151990634029Sdyoung } while (0)
152090634029Sdyoung #else /* !IEEE80211_DEBUG */
152190634029Sdyoung #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
152290634029Sdyoung if ((_ssid)[1] != 0 && \
152390634029Sdyoung ((_ssid)[1] != (_ni)->ni_esslen || \
152490634029Sdyoung memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
152590634029Sdyoung ic->ic_stats.is_rx_ssidmismatch++; \
152690634029Sdyoung return; \
152790634029Sdyoung } \
152890634029Sdyoung } while (0)
152990634029Sdyoung #endif /* !IEEE80211_DEBUG */
153090634029Sdyoung
1531bf2739eaSchristos /* unaligned little endian access */
153290634029Sdyoung #define LE_READ_2(p) \
153390634029Sdyoung ((u_int16_t) \
153490634029Sdyoung ((((const u_int8_t *)(p))[0] ) | \
153590634029Sdyoung (((const u_int8_t *)(p))[1] << 8)))
153690634029Sdyoung #define LE_READ_4(p) \
153790634029Sdyoung ((u_int32_t) \
153890634029Sdyoung ((((const u_int8_t *)(p))[0] ) | \
153990634029Sdyoung (((const u_int8_t *)(p))[1] << 8) | \
154090634029Sdyoung (((const u_int8_t *)(p))[2] << 16) | \
154190634029Sdyoung (((const u_int8_t *)(p))[3] << 24)))
154290634029Sdyoung
1543989f66d1Schristos static __inline int
iswpaoui(const u_int8_t * frm)154490634029Sdyoung iswpaoui(const u_int8_t *frm)
154590634029Sdyoung {
154690634029Sdyoung return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
154790634029Sdyoung }
154890634029Sdyoung
1549989f66d1Schristos static __inline int
iswmeoui(const u_int8_t * frm)155090634029Sdyoung iswmeoui(const u_int8_t *frm)
155190634029Sdyoung {
155290634029Sdyoung return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
155390634029Sdyoung }
155490634029Sdyoung
1555989f66d1Schristos static __inline int
iswmeparam(const u_int8_t * frm)155690634029Sdyoung iswmeparam(const u_int8_t *frm)
155790634029Sdyoung {
155890634029Sdyoung return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
155990634029Sdyoung frm[6] == WME_PARAM_OUI_SUBTYPE;
156090634029Sdyoung }
156190634029Sdyoung
1562989f66d1Schristos static __inline int
iswmeinfo(const u_int8_t * frm)156390634029Sdyoung iswmeinfo(const u_int8_t *frm)
156490634029Sdyoung {
156590634029Sdyoung return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
156690634029Sdyoung frm[6] == WME_INFO_OUI_SUBTYPE;
156790634029Sdyoung }
156890634029Sdyoung
156990634029Sdyoung /*
157090634029Sdyoung * Convert a WPA cipher selector OUI to an internal
157190634029Sdyoung * cipher algorithm. Where appropriate we also
157290634029Sdyoung * record any key length.
157390634029Sdyoung */
157490634029Sdyoung static int
wpa_cipher(u_int8_t * sel,u_int8_t * keylen)157590634029Sdyoung wpa_cipher(u_int8_t *sel, u_int8_t *keylen)
157690634029Sdyoung {
157790634029Sdyoung #define WPA_SEL(x) (((x)<<24)|WPA_OUI)
157890634029Sdyoung u_int32_t w = LE_READ_4(sel);
157990634029Sdyoung
158090634029Sdyoung switch (w) {
158190634029Sdyoung case WPA_SEL(WPA_CSE_NULL):
158290634029Sdyoung return IEEE80211_CIPHER_NONE;
158390634029Sdyoung case WPA_SEL(WPA_CSE_WEP40):
158490634029Sdyoung if (keylen)
158590634029Sdyoung *keylen = 40 / NBBY;
158690634029Sdyoung return IEEE80211_CIPHER_WEP;
158790634029Sdyoung case WPA_SEL(WPA_CSE_WEP104):
158890634029Sdyoung if (keylen)
158990634029Sdyoung *keylen = 104 / NBBY;
159090634029Sdyoung return IEEE80211_CIPHER_WEP;
159190634029Sdyoung case WPA_SEL(WPA_CSE_TKIP):
159290634029Sdyoung return IEEE80211_CIPHER_TKIP;
159390634029Sdyoung case WPA_SEL(WPA_CSE_CCMP):
159490634029Sdyoung return IEEE80211_CIPHER_AES_CCM;
159590634029Sdyoung }
159690634029Sdyoung return 32; /* NB: so 1<< is discarded */
159790634029Sdyoung #undef WPA_SEL
159890634029Sdyoung }
159990634029Sdyoung
160090634029Sdyoung /*
160190634029Sdyoung * Convert a WPA key management/authentication algorithm
160290634029Sdyoung * to an internal code.
160390634029Sdyoung */
160490634029Sdyoung static int
wpa_keymgmt(u_int8_t * sel)160590634029Sdyoung wpa_keymgmt(u_int8_t *sel)
160690634029Sdyoung {
160790634029Sdyoung #define WPA_SEL(x) (((x)<<24)|WPA_OUI)
160890634029Sdyoung u_int32_t w = LE_READ_4(sel);
160990634029Sdyoung
161090634029Sdyoung switch (w) {
161190634029Sdyoung case WPA_SEL(WPA_ASE_8021X_UNSPEC):
161290634029Sdyoung return WPA_ASE_8021X_UNSPEC;
161390634029Sdyoung case WPA_SEL(WPA_ASE_8021X_PSK):
161490634029Sdyoung return WPA_ASE_8021X_PSK;
161590634029Sdyoung case WPA_SEL(WPA_ASE_NONE):
161690634029Sdyoung return WPA_ASE_NONE;
161790634029Sdyoung }
161890634029Sdyoung return 0; /* NB: so is discarded */
161990634029Sdyoung #undef WPA_SEL
162090634029Sdyoung }
162190634029Sdyoung
162290634029Sdyoung /*
162390634029Sdyoung * Parse a WPA information element to collect parameters
162490634029Sdyoung * and validate the parameters against what has been
162590634029Sdyoung * configured for the system.
162690634029Sdyoung */
162790634029Sdyoung static int
ieee80211_parse_wpa(struct ieee80211com * ic,u_int8_t * frm,struct ieee80211_rsnparms * rsn,const struct ieee80211_frame * wh)162890634029Sdyoung ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm,
162990634029Sdyoung struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
163090634029Sdyoung {
163190634029Sdyoung u_int8_t len = frm[1];
163290634029Sdyoung u_int32_t w;
163390634029Sdyoung int n;
163490634029Sdyoung
163587515e34Sskrll if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) {
163687515e34Sskrll IEEE80211_DISCARD_IE(ic,
163787515e34Sskrll IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
163887515e34Sskrll wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags);
163987515e34Sskrll return IEEE80211_REASON_IE_INVALID;
164087515e34Sskrll }
1641a45976c2Smaxv
1642a45976c2Smaxv /*
1643a45976c2Smaxv * Check the length once for fixed parts: OUI, type,
1644a45976c2Smaxv * version, mcast cipher, and 2 selector counts.
1645a45976c2Smaxv * Other, variable-length data, must be checked separately.
1646a45976c2Smaxv */
164790634029Sdyoung if (len < 14) {
164890634029Sdyoung IEEE80211_DISCARD_IE(ic,
164990634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
165090634029Sdyoung wh, "WPA", "too short, len %u", len);
165190634029Sdyoung return IEEE80211_REASON_IE_INVALID;
165290634029Sdyoung }
1653a45976c2Smaxv
1654a45976c2Smaxv frm += 2; /* beginning of payload */
1655a45976c2Smaxv frm += 4, len -= 4;
1656a45976c2Smaxv
165790634029Sdyoung /* NB: iswapoui already validated the OUI and type */
165890634029Sdyoung w = LE_READ_2(frm);
165990634029Sdyoung if (w != WPA_VERSION) {
166090634029Sdyoung IEEE80211_DISCARD_IE(ic,
166190634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
166290634029Sdyoung wh, "WPA", "bad version %u", w);
166390634029Sdyoung return IEEE80211_REASON_IE_INVALID;
166490634029Sdyoung }
166590634029Sdyoung frm += 2, len -= 2;
166690634029Sdyoung
166790634029Sdyoung /* multicast/group cipher */
166890634029Sdyoung w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
166990634029Sdyoung if (w != rsn->rsn_mcastcipher) {
167090634029Sdyoung IEEE80211_DISCARD_IE(ic,
167190634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
167290634029Sdyoung wh, "WPA", "mcast cipher mismatch; got %u, expected %u",
167390634029Sdyoung w, rsn->rsn_mcastcipher);
167490634029Sdyoung return IEEE80211_REASON_IE_INVALID;
167590634029Sdyoung }
167690634029Sdyoung frm += 4, len -= 4;
167790634029Sdyoung
167890634029Sdyoung /* unicast ciphers */
167990634029Sdyoung n = LE_READ_2(frm);
168090634029Sdyoung frm += 2, len -= 2;
168190634029Sdyoung if (len < n*4+2) {
168290634029Sdyoung IEEE80211_DISCARD_IE(ic,
168390634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
168490634029Sdyoung wh, "WPA", "ucast cipher data too short; len %u, n %u",
168590634029Sdyoung len, n);
168690634029Sdyoung return IEEE80211_REASON_IE_INVALID;
168790634029Sdyoung }
168890634029Sdyoung w = 0;
168990634029Sdyoung for (; n > 0; n--) {
169090634029Sdyoung w |= 1 << wpa_cipher(frm, &rsn->rsn_ucastkeylen);
169190634029Sdyoung frm += 4, len -= 4;
169290634029Sdyoung }
169390634029Sdyoung w &= rsn->rsn_ucastcipherset;
169490634029Sdyoung if (w == 0) {
169590634029Sdyoung IEEE80211_DISCARD_IE(ic,
169690634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
169790634029Sdyoung wh, "WPA", "%s", "ucast cipher set empty");
169890634029Sdyoung return IEEE80211_REASON_IE_INVALID;
169990634029Sdyoung }
170090634029Sdyoung if (w & (1 << IEEE80211_CIPHER_TKIP))
170190634029Sdyoung rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
170290634029Sdyoung else
170390634029Sdyoung rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
170490634029Sdyoung
170590634029Sdyoung /* key management algorithms */
170690634029Sdyoung n = LE_READ_2(frm);
170790634029Sdyoung frm += 2, len -= 2;
170890634029Sdyoung if (len < n*4) {
170990634029Sdyoung IEEE80211_DISCARD_IE(ic,
171090634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
171190634029Sdyoung wh, "WPA", "key mgmt alg data too short; len %u, n %u",
171290634029Sdyoung len, n);
171390634029Sdyoung return IEEE80211_REASON_IE_INVALID;
171490634029Sdyoung }
171590634029Sdyoung w = 0;
171690634029Sdyoung for (; n > 0; n--) {
171790634029Sdyoung w |= wpa_keymgmt(frm);
171890634029Sdyoung frm += 4, len -= 4;
171990634029Sdyoung }
172090634029Sdyoung w &= rsn->rsn_keymgmtset;
172190634029Sdyoung if (w == 0) {
172290634029Sdyoung IEEE80211_DISCARD_IE(ic,
172390634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
172490634029Sdyoung wh, "WPA", "%s", "no acceptable key mgmt alg");
172590634029Sdyoung return IEEE80211_REASON_IE_INVALID;
172690634029Sdyoung }
172790634029Sdyoung if (w & WPA_ASE_8021X_UNSPEC)
172890634029Sdyoung rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
172990634029Sdyoung else
173090634029Sdyoung rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
173190634029Sdyoung
173290634029Sdyoung if (len > 2) /* optional capabilities */
173390634029Sdyoung rsn->rsn_caps = LE_READ_2(frm);
173490634029Sdyoung
173590634029Sdyoung return 0;
173690634029Sdyoung }
173790634029Sdyoung
173890634029Sdyoung /*
173990634029Sdyoung * Convert an RSN cipher selector OUI to an internal
174090634029Sdyoung * cipher algorithm. Where appropriate we also
174190634029Sdyoung * record any key length.
174290634029Sdyoung */
174390634029Sdyoung static int
rsn_cipher(u_int8_t * sel,u_int8_t * keylen)174490634029Sdyoung rsn_cipher(u_int8_t *sel, u_int8_t *keylen)
174590634029Sdyoung {
174690634029Sdyoung #define RSN_SEL(x) (((x)<<24)|RSN_OUI)
174790634029Sdyoung u_int32_t w = LE_READ_4(sel);
174890634029Sdyoung
174990634029Sdyoung switch (w) {
175090634029Sdyoung case RSN_SEL(RSN_CSE_NULL):
175190634029Sdyoung return IEEE80211_CIPHER_NONE;
175290634029Sdyoung case RSN_SEL(RSN_CSE_WEP40):
175390634029Sdyoung if (keylen)
175490634029Sdyoung *keylen = 40 / NBBY;
175590634029Sdyoung return IEEE80211_CIPHER_WEP;
175690634029Sdyoung case RSN_SEL(RSN_CSE_WEP104):
175790634029Sdyoung if (keylen)
175890634029Sdyoung *keylen = 104 / NBBY;
175990634029Sdyoung return IEEE80211_CIPHER_WEP;
176090634029Sdyoung case RSN_SEL(RSN_CSE_TKIP):
176190634029Sdyoung return IEEE80211_CIPHER_TKIP;
176290634029Sdyoung case RSN_SEL(RSN_CSE_CCMP):
176390634029Sdyoung return IEEE80211_CIPHER_AES_CCM;
176490634029Sdyoung case RSN_SEL(RSN_CSE_WRAP):
176590634029Sdyoung return IEEE80211_CIPHER_AES_OCB;
176690634029Sdyoung }
176790634029Sdyoung return 32; /* NB: so 1<< is discarded */
176890634029Sdyoung #undef WPA_SEL
176990634029Sdyoung }
177090634029Sdyoung
177190634029Sdyoung /*
177290634029Sdyoung * Convert an RSN key management/authentication algorithm
177390634029Sdyoung * to an internal code.
177490634029Sdyoung */
177590634029Sdyoung static int
rsn_keymgmt(u_int8_t * sel)177690634029Sdyoung rsn_keymgmt(u_int8_t *sel)
177790634029Sdyoung {
177890634029Sdyoung #define RSN_SEL(x) (((x)<<24)|RSN_OUI)
177990634029Sdyoung u_int32_t w = LE_READ_4(sel);
178090634029Sdyoung
178190634029Sdyoung switch (w) {
178290634029Sdyoung case RSN_SEL(RSN_ASE_8021X_UNSPEC):
178390634029Sdyoung return RSN_ASE_8021X_UNSPEC;
178490634029Sdyoung case RSN_SEL(RSN_ASE_8021X_PSK):
178590634029Sdyoung return RSN_ASE_8021X_PSK;
178690634029Sdyoung case RSN_SEL(RSN_ASE_NONE):
178790634029Sdyoung return RSN_ASE_NONE;
178890634029Sdyoung }
178990634029Sdyoung return 0; /* NB: so is discarded */
179090634029Sdyoung #undef RSN_SEL
179190634029Sdyoung }
179290634029Sdyoung
179390634029Sdyoung /*
179490634029Sdyoung * Parse a WPA/RSN information element to collect parameters
179590634029Sdyoung * and validate the parameters against what has been
179690634029Sdyoung * configured for the system.
179790634029Sdyoung */
179890634029Sdyoung static int
ieee80211_parse_rsn(struct ieee80211com * ic,u_int8_t * frm,struct ieee80211_rsnparms * rsn,const struct ieee80211_frame * wh)179990634029Sdyoung ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm,
180090634029Sdyoung struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
180190634029Sdyoung {
180290634029Sdyoung u_int8_t len = frm[1];
180390634029Sdyoung u_int32_t w;
180490634029Sdyoung int n;
180590634029Sdyoung
180687515e34Sskrll if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) {
180787515e34Sskrll IEEE80211_DISCARD_IE(ic,
180887515e34Sskrll IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
180987515e34Sskrll wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags);
181087515e34Sskrll return IEEE80211_REASON_IE_INVALID;
181187515e34Sskrll }
1812a45976c2Smaxv
1813a45976c2Smaxv /*
1814a45976c2Smaxv * Check the length once for fixed parts:
1815a45976c2Smaxv * version, mcast cipher, and 2 selector counts.
1816a45976c2Smaxv * Other, variable-length data, must be checked separately.
1817a45976c2Smaxv */
181890634029Sdyoung if (len < 10) {
181990634029Sdyoung IEEE80211_DISCARD_IE(ic,
182090634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
182190634029Sdyoung wh, "RSN", "too short, len %u", len);
182290634029Sdyoung return IEEE80211_REASON_IE_INVALID;
182390634029Sdyoung }
1824a45976c2Smaxv
1825a45976c2Smaxv frm += 2; /* beginning of payload */
182690634029Sdyoung w = LE_READ_2(frm);
182790634029Sdyoung if (w != RSN_VERSION) {
182890634029Sdyoung IEEE80211_DISCARD_IE(ic,
182990634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
183090634029Sdyoung wh, "RSN", "bad version %u", w);
183190634029Sdyoung return IEEE80211_REASON_IE_INVALID;
183290634029Sdyoung }
183390634029Sdyoung frm += 2, len -= 2;
183490634029Sdyoung
183590634029Sdyoung /* multicast/group cipher */
183690634029Sdyoung w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
183790634029Sdyoung if (w != rsn->rsn_mcastcipher) {
183890634029Sdyoung IEEE80211_DISCARD_IE(ic,
183990634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
184090634029Sdyoung wh, "RSN", "mcast cipher mismatch; got %u, expected %u",
184190634029Sdyoung w, rsn->rsn_mcastcipher);
184290634029Sdyoung return IEEE80211_REASON_IE_INVALID;
184390634029Sdyoung }
184490634029Sdyoung frm += 4, len -= 4;
184590634029Sdyoung
184690634029Sdyoung /* unicast ciphers */
184790634029Sdyoung n = LE_READ_2(frm);
184890634029Sdyoung frm += 2, len -= 2;
184990634029Sdyoung if (len < n*4+2) {
185090634029Sdyoung IEEE80211_DISCARD_IE(ic,
185190634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
185290634029Sdyoung wh, "RSN", "ucast cipher data too short; len %u, n %u",
185390634029Sdyoung len, n);
185490634029Sdyoung return IEEE80211_REASON_IE_INVALID;
185590634029Sdyoung }
185690634029Sdyoung w = 0;
185790634029Sdyoung for (; n > 0; n--) {
185890634029Sdyoung w |= 1 << rsn_cipher(frm, &rsn->rsn_ucastkeylen);
185990634029Sdyoung frm += 4, len -= 4;
186090634029Sdyoung }
186190634029Sdyoung w &= rsn->rsn_ucastcipherset;
186290634029Sdyoung if (w == 0) {
186390634029Sdyoung IEEE80211_DISCARD_IE(ic,
186490634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
186590634029Sdyoung wh, "RSN", "%s", "ucast cipher set empty");
186690634029Sdyoung return IEEE80211_REASON_IE_INVALID;
186790634029Sdyoung }
186890634029Sdyoung if (w & (1 << IEEE80211_CIPHER_TKIP))
186990634029Sdyoung rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
187090634029Sdyoung else
187190634029Sdyoung rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
187290634029Sdyoung
187390634029Sdyoung /* key management algorithms */
187490634029Sdyoung n = LE_READ_2(frm);
187590634029Sdyoung frm += 2, len -= 2;
187690634029Sdyoung if (len < n*4) {
187790634029Sdyoung IEEE80211_DISCARD_IE(ic,
187890634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
187990634029Sdyoung wh, "RSN", "key mgmt alg data too short; len %u, n %u",
188090634029Sdyoung len, n);
188190634029Sdyoung return IEEE80211_REASON_IE_INVALID;
188290634029Sdyoung }
188390634029Sdyoung w = 0;
188490634029Sdyoung for (; n > 0; n--) {
188590634029Sdyoung w |= rsn_keymgmt(frm);
188690634029Sdyoung frm += 4, len -= 4;
188790634029Sdyoung }
188890634029Sdyoung w &= rsn->rsn_keymgmtset;
188990634029Sdyoung if (w == 0) {
189090634029Sdyoung IEEE80211_DISCARD_IE(ic,
189190634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
189290634029Sdyoung wh, "RSN", "%s", "no acceptable key mgmt alg");
189390634029Sdyoung return IEEE80211_REASON_IE_INVALID;
189490634029Sdyoung }
189590634029Sdyoung if (w & RSN_ASE_8021X_UNSPEC)
189690634029Sdyoung rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
189790634029Sdyoung else
189890634029Sdyoung rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
189990634029Sdyoung
190090634029Sdyoung /* optional RSN capabilities */
190190634029Sdyoung if (len > 2)
190290634029Sdyoung rsn->rsn_caps = LE_READ_2(frm);
190390634029Sdyoung /* XXXPMKID */
190490634029Sdyoung
190590634029Sdyoung return 0;
190690634029Sdyoung }
190790634029Sdyoung
190890634029Sdyoung static int
ieee80211_parse_wmeparams(struct ieee80211com * ic,u_int8_t * frm,const struct ieee80211_frame * wh)190990634029Sdyoung ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm,
191090634029Sdyoung const struct ieee80211_frame *wh)
191190634029Sdyoung {
191290634029Sdyoung #define MS(_v, _f) (((_v) & _f) >> _f##_S)
191390634029Sdyoung struct ieee80211_wme_state *wme = &ic->ic_wme;
191490634029Sdyoung u_int len = frm[1], qosinfo;
191590634029Sdyoung int i;
191690634029Sdyoung
191790634029Sdyoung if (len < sizeof(struct ieee80211_wme_param)-2) {
191890634029Sdyoung IEEE80211_DISCARD_IE(ic,
191990634029Sdyoung IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
192090634029Sdyoung wh, "WME", "too short, len %u", len);
192190634029Sdyoung return -1;
192290634029Sdyoung }
192311a42b5cSmaxv
192442c42085Schristos qosinfo = frm[offsetof(struct ieee80211_wme_param, param_qosInfo)];
192590634029Sdyoung qosinfo &= WME_QOSINFO_COUNT;
192611a42b5cSmaxv
192790634029Sdyoung /* XXX do proper check for wraparound */
192890634029Sdyoung if (qosinfo == wme->wme_wmeChanParams.cap_info)
192990634029Sdyoung return 0;
193011a42b5cSmaxv
193142c42085Schristos frm += offsetof(struct ieee80211_wme_param, params_acParams);
193290634029Sdyoung for (i = 0; i < WME_NUM_AC; i++) {
193390634029Sdyoung struct wmeParams *wmep =
193490634029Sdyoung &wme->wme_wmeChanParams.cap_wmeParams[i];
193590634029Sdyoung /* NB: ACI not used */
193690634029Sdyoung wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
193790634029Sdyoung wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
193890634029Sdyoung wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
193990634029Sdyoung wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
194090634029Sdyoung wmep->wmep_txopLimit = LE_READ_2(frm+2);
194190634029Sdyoung frm += 4;
194290634029Sdyoung }
194311a42b5cSmaxv
194490634029Sdyoung wme->wme_wmeChanParams.cap_info = qosinfo;
194590634029Sdyoung return 1;
194690634029Sdyoung #undef MS
194790634029Sdyoung }
194890634029Sdyoung
194987515e34Sskrll void
ieee80211_saveie(u_int8_t ** iep,const u_int8_t * ie)195090634029Sdyoung ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie)
195190634029Sdyoung {
195290634029Sdyoung u_int ielen = ie[1]+2;
1953a45976c2Smaxv
1954a45976c2Smaxv /* Record information element for later use. */
195590634029Sdyoung if (*iep == NULL || (*iep)[1] != ie[1]) {
195690634029Sdyoung if (*iep != NULL)
19579b87d582Scegger free(*iep, M_DEVBUF);
19585a57baa4Schristos *iep = malloc(ielen, M_DEVBUF, M_NOWAIT);
195990634029Sdyoung }
196090634029Sdyoung if (*iep != NULL)
196190634029Sdyoung memcpy(*iep, ie, ielen);
196290634029Sdyoung /* XXX note failure */
196390634029Sdyoung }
196490634029Sdyoung
19657b6a25edSdyoung static void
ieee80211_update_adhoc_node(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_frame * wh,struct ieee80211_scanparams * scan,int rssi,u_int32_t rstamp)19667b6a25edSdyoung ieee80211_update_adhoc_node(struct ieee80211com *ic, struct ieee80211_node *ni,
19677b6a25edSdyoung struct ieee80211_frame *wh, struct ieee80211_scanparams *scan, int rssi,
19687b6a25edSdyoung u_int32_t rstamp)
19697b6a25edSdyoung {
19707b6a25edSdyoung if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
19717b6a25edSdyoung /*
19727b6a25edSdyoung * Create a new entry in the neighbor table.
19737b6a25edSdyoung * Records the TSF.
19747b6a25edSdyoung */
19757b6a25edSdyoung if ((ni = ieee80211_add_neighbor(ic, wh, scan)) == NULL)
19767b6a25edSdyoung return;
19777b6a25edSdyoung } else if (ni->ni_capinfo == 0) {
19787b6a25edSdyoung /*
19797b6a25edSdyoung * Initialize a node that was "faked up." Records
19807b6a25edSdyoung * the TSF.
19817b6a25edSdyoung *
19827b6a25edSdyoung * No need to check for a change of BSSID: ni could
19837b6a25edSdyoung * not have been the IBSS (ic_bss)
19847b6a25edSdyoung */
19857b6a25edSdyoung ieee80211_init_neighbor(ic, ni, wh, scan, 0);
19867b6a25edSdyoung } else {
19877b6a25edSdyoung /* Record TSF for potential resync. */
19881df75eefSmaxv memcpy(ni->ni_tstamp.data, scan->sp_tstamp, sizeof(ni->ni_tstamp));
19897b6a25edSdyoung }
19907b6a25edSdyoung
19917b6a25edSdyoung ni->ni_rssi = rssi;
19927b6a25edSdyoung ni->ni_rstamp = rstamp;
19937b6a25edSdyoung
19947b6a25edSdyoung /* Mark a neighbor's change of BSSID. */
19957b6a25edSdyoung if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid))
19967b6a25edSdyoung return;
19977b6a25edSdyoung
19987b6a25edSdyoung IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
19997b6a25edSdyoung
20007b6a25edSdyoung if (ni != ic->ic_bss)
20017b6a25edSdyoung return;
20027b6a25edSdyoung else if (ic->ic_flags & IEEE80211_F_DESBSSID) {
20037b6a25edSdyoung /*
20047b6a25edSdyoung * Now, ni does not represent a network we
20057b6a25edSdyoung * want to belong to, so start a scan.
20067b6a25edSdyoung */
20077b6a25edSdyoung ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
20087b6a25edSdyoung return;
20097b6a25edSdyoung } else {
20107b6a25edSdyoung /*
20117b6a25edSdyoung * A RUN->RUN transition lets the driver
20127b6a25edSdyoung * reprogram its BSSID filter.
20137b6a25edSdyoung *
20147b6a25edSdyoung * No need to SCAN, we already belong to
20157b6a25edSdyoung * an IBSS that meets our criteria: channel,
20167b6a25edSdyoung * SSID, etc. It could be harmful to scan,
20177b6a25edSdyoung * too: if a scan does not detect nodes
20187b6a25edSdyoung * belonging to my current IBSS, then we
20197b6a25edSdyoung * will create a new IBSS at the end of
20207b6a25edSdyoung * the scan, needlessly splitting the
20217b6a25edSdyoung * network.
20227b6a25edSdyoung */
20237b6a25edSdyoung ieee80211_new_state(ic, IEEE80211_S_RUN, 0);
20247b6a25edSdyoung }
20257b6a25edSdyoung }
20267b6a25edSdyoung
202785acfa9cSmaxv /* -------------------------------------------------------------------------- */
202885acfa9cSmaxv
20291551d983Smaxv #define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
20301551d983Smaxv if ((__elem) == NULL) { \
20311551d983Smaxv IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
20321551d983Smaxv wh, ieee80211_mgt_subtype_name[subtype >> \
20331551d983Smaxv IEEE80211_FC0_SUBTYPE_SHIFT], \
20341551d983Smaxv "%s", "no " #__elem ); \
20351551d983Smaxv ic->ic_stats.is_rx_elem_missing++; \
20361551d983Smaxv return; \
20371551d983Smaxv } \
20381551d983Smaxv if ((__elem)[1] > (__maxlen)) { \
20391551d983Smaxv IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
20401551d983Smaxv wh, ieee80211_mgt_subtype_name[subtype >> \
20411551d983Smaxv IEEE80211_FC0_SUBTYPE_SHIFT], \
20421551d983Smaxv "bad " #__elem " len %d", (__elem)[1]); \
20431551d983Smaxv ic->ic_stats.is_rx_elem_toobig++; \
20441551d983Smaxv return; \
20451551d983Smaxv } \
20461551d983Smaxv } while (0)
20471551d983Smaxv
20481551d983Smaxv #define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
2049a45976c2Smaxv if ((size_t)(_len) < (size_t)(_minlen)) { \
20501551d983Smaxv IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
20511551d983Smaxv wh, ieee80211_mgt_subtype_name[subtype >> \
20521551d983Smaxv IEEE80211_FC0_SUBTYPE_SHIFT], \
20531551d983Smaxv "%s", "ie too short"); \
20541551d983Smaxv ic->ic_stats.is_rx_elem_toosmall++; \
20551551d983Smaxv return; \
20561551d983Smaxv } \
20571551d983Smaxv } while (0)
20581551d983Smaxv
205985acfa9cSmaxv static void
ieee80211_recv_mgmt_beacon(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)206085acfa9cSmaxv ieee80211_recv_mgmt_beacon(struct ieee80211com *ic, struct mbuf *m0,
206124fb50b3Smaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
206240e261aaSdyoung {
206340e261aaSdyoung struct ieee80211_frame *wh;
206440e261aaSdyoung u_int8_t *frm, *efrm;
2065c2ec5838Schristos IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
206685acfa9cSmaxv struct ieee80211_scanparams scan;
206740e261aaSdyoung
206840e261aaSdyoung wh = mtod(m0, struct ieee80211_frame *);
206924fb50b3Smaxv frm = (u_int8_t *)(wh + 1);
207040e261aaSdyoung efrm = mtod(m0, u_int8_t *) + m0->m_len;
207124fb50b3Smaxv
207288a8480eSmycroft /*
207390634029Sdyoung * We process beacon/probe response frames:
207490634029Sdyoung * o when scanning, or
207590634029Sdyoung * o station mode when associated (to collect state
207690634029Sdyoung * updates such as 802.11g slot time), or
207790634029Sdyoung * o adhoc mode (to discover neighbors)
207890634029Sdyoung * Frames otherwise received are discarded.
207988a8480eSmycroft */
208090634029Sdyoung if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
208190634029Sdyoung (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) ||
208290634029Sdyoung ic->ic_opmode == IEEE80211_M_IBSS)) {
208390634029Sdyoung ic->ic_stats.is_rx_mgtdiscard++;
208490634029Sdyoung return;
208540e261aaSdyoung }
208624fb50b3Smaxv
208740e261aaSdyoung /*
208840e261aaSdyoung * beacon/probe response frame format
208940e261aaSdyoung * [8] time stamp
209040e261aaSdyoung * [2] beacon interval
209140e261aaSdyoung * [2] capability information
209240e261aaSdyoung * [tlv] ssid
209340e261aaSdyoung * [tlv] supported rates
209440e261aaSdyoung * [tlv] country information
209540e261aaSdyoung * [tlv] parameter set (FH/DS)
209640e261aaSdyoung * [tlv] erp information
209740e261aaSdyoung * [tlv] extended supported rates
209890634029Sdyoung * [tlv] WME
209990634029Sdyoung * [tlv] WPA or RSN
210040e261aaSdyoung */
210140e261aaSdyoung IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
210287515e34Sskrll memset(&scan, 0, sizeof(scan));
21031df75eefSmaxv scan.sp_tstamp = frm; frm += 8;
21041df75eefSmaxv scan.sp_bintval = le16toh(*(u_int16_t *)frm); frm += 2;
21051df75eefSmaxv scan.sp_capinfo = le16toh(*(u_int16_t *)frm); frm += 2;
21061df75eefSmaxv scan.sp_bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
21071df75eefSmaxv scan.sp_chan = scan.sp_bchan;
210887515e34Sskrll
210924fb50b3Smaxv while (frm + 1 < efrm) {
211024fb50b3Smaxv IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
211124fb50b3Smaxv
211240e261aaSdyoung switch (*frm) {
211340e261aaSdyoung case IEEE80211_ELEMID_SSID:
21143b005d6bSmaxv /* no length check needed */
21151df75eefSmaxv scan.sp_ssid = frm;
211640e261aaSdyoung break;
211740e261aaSdyoung case IEEE80211_ELEMID_RATES:
21183b005d6bSmaxv /* no length check needed */
21191df75eefSmaxv scan.sp_rates = frm;
212040e261aaSdyoung break;
212140e261aaSdyoung case IEEE80211_ELEMID_COUNTRY:
21223b005d6bSmaxv /* XXX: we don't do anything with this? */
21231df75eefSmaxv scan.sp_country = frm;
212440e261aaSdyoung break;
212540e261aaSdyoung case IEEE80211_ELEMID_FHPARMS:
2126e54b750fSmaxv IEEE80211_VERIFY_LENGTH(frm[1], 5);
212740e261aaSdyoung if (ic->ic_phytype == IEEE80211_T_FH) {
21281df75eefSmaxv scan.sp_fhdwell = LE_READ_2(&frm[2]);
21291df75eefSmaxv scan.sp_chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
21301df75eefSmaxv scan.sp_fhindex = frm[6];
213140e261aaSdyoung }
213240e261aaSdyoung break;
213340e261aaSdyoung case IEEE80211_ELEMID_DSPARMS:
213440e261aaSdyoung /*
213540e261aaSdyoung * XXX hack this since depending on phytype
213640e261aaSdyoung * is problematic for multi-mode devices.
213740e261aaSdyoung */
2138e54b750fSmaxv IEEE80211_VERIFY_LENGTH(frm[1], 1);
213940e261aaSdyoung if (ic->ic_phytype != IEEE80211_T_FH)
21401df75eefSmaxv scan.sp_chan = frm[2];
214140e261aaSdyoung break;
214240e261aaSdyoung case IEEE80211_ELEMID_TIM:
214390634029Sdyoung /* XXX ATIM? */
214403cb4450Smaxv IEEE80211_VERIFY_LENGTH(frm[1], 4);
21451df75eefSmaxv scan.sp_tim = frm;
21461df75eefSmaxv scan.sp_timoff = frm - mtod(m0, u_int8_t *);
214740e261aaSdyoung break;
21481d6dd12bSdyoung case IEEE80211_ELEMID_IBSSPARMS:
21491d6dd12bSdyoung break;
215040e261aaSdyoung case IEEE80211_ELEMID_XRATES:
21511df75eefSmaxv scan.sp_xrates = frm;
215240e261aaSdyoung break;
215340e261aaSdyoung case IEEE80211_ELEMID_ERP:
215440e261aaSdyoung if (frm[1] != 1) {
21550170309aSmaxv IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
21560170309aSmaxv wh, "ERP", "bad len %u", frm[1]);
21579280f4b4Sdyoung ic->ic_stats.is_rx_elem_toobig++;
215840e261aaSdyoung break;
215940e261aaSdyoung }
21601df75eefSmaxv scan.sp_erp = frm[2];
216140e261aaSdyoung break;
216290634029Sdyoung case IEEE80211_ELEMID_RSN:
21633b005d6bSmaxv /* no length check needed */
21641df75eefSmaxv scan.sp_wpa = frm;
216590634029Sdyoung break;
216690634029Sdyoung case IEEE80211_ELEMID_VENDOR:
21673b005d6bSmaxv /* no length check needed */
216890634029Sdyoung if (iswpaoui(frm))
21691df75eefSmaxv scan.sp_wpa = frm;
217090634029Sdyoung else if (iswmeparam(frm) || iswmeinfo(frm))
21711df75eefSmaxv scan.sp_wme = frm;
217290634029Sdyoung /* XXX Atheros OUI support */
217390634029Sdyoung break;
217440e261aaSdyoung default:
217590634029Sdyoung IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
21760170309aSmaxv wh, "unhandled", "id %u, len %u", *frm, frm[1]);
21779280f4b4Sdyoung ic->ic_stats.is_rx_elem_unknown++;
217840e261aaSdyoung break;
217940e261aaSdyoung }
218024fb50b3Smaxv
218140e261aaSdyoung frm += frm[1] + 2;
218240e261aaSdyoung }
218324fb50b3Smaxv
21841df75eefSmaxv IEEE80211_VERIFY_ELEMENT(scan.sp_rates, IEEE80211_RATE_MAXSIZE);
21851df75eefSmaxv IEEE80211_VERIFY_ELEMENT(scan.sp_ssid, IEEE80211_NWID_LEN);
218624fb50b3Smaxv
218740e261aaSdyoung if (
218840e261aaSdyoung #if IEEE80211_CHAN_MAX < 255
21891df75eefSmaxv scan.sp_chan > IEEE80211_CHAN_MAX ||
219040e261aaSdyoung #endif
21911df75eefSmaxv isclr(ic->ic_chan_active, scan.sp_chan)) {
219287515e34Sskrll IEEE80211_DISCARD(ic,
219387515e34Sskrll IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
219490634029Sdyoung wh, ieee80211_mgt_subtype_name[subtype >>
219590634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT],
21961df75eefSmaxv "invalid channel %u", scan.sp_chan);
21979280f4b4Sdyoung ic->ic_stats.is_rx_badchan++;
219840e261aaSdyoung return;
219940e261aaSdyoung }
22000170309aSmaxv
22011df75eefSmaxv if (scan.sp_chan != scan.sp_bchan &&
220287515e34Sskrll ic->ic_phytype != IEEE80211_T_FH) {
220340e261aaSdyoung /*
220440e261aaSdyoung * Frame was received on a channel different from the
2205b001a515Sonoe * one indicated in the DS params element id;
220640e261aaSdyoung * silently discard it.
220740e261aaSdyoung *
220840e261aaSdyoung * NB: this can happen due to signal leakage.
2209b001a515Sonoe * But we should take it for FH phy because
2210b001a515Sonoe * the rssi value should be correct even for
2211b001a515Sonoe * different hop pattern in FH.
221240e261aaSdyoung */
221387515e34Sskrll IEEE80211_DISCARD(ic,
221487515e34Sskrll IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
221590634029Sdyoung wh, ieee80211_mgt_subtype_name[subtype >>
221690634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT],
22171df75eefSmaxv "for off-channel %u", scan.sp_chan);
22189280f4b4Sdyoung ic->ic_stats.is_rx_chanmismatch++;
221940e261aaSdyoung return;
222040e261aaSdyoung }
22210170309aSmaxv
22221df75eefSmaxv if (!(IEEE80211_BINTVAL_MIN <= scan.sp_bintval &&
22231df75eefSmaxv scan.sp_bintval <= IEEE80211_BINTVAL_MAX)) {
222487515e34Sskrll IEEE80211_DISCARD(ic,
222587515e34Sskrll IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
222687515e34Sskrll wh, ieee80211_mgt_subtype_name[subtype >>
222787515e34Sskrll IEEE80211_FC0_SUBTYPE_SHIFT],
22281df75eefSmaxv "bogus beacon interval", scan.sp_bintval);
222987515e34Sskrll ic->ic_stats.is_rx_badbintval++;
223087515e34Sskrll return;
223187515e34Sskrll }
223240e261aaSdyoung
2233fdf8c16fSdyoung if (ni != ic->ic_bss) {
2234fdf8c16fSdyoung ni = ieee80211_refine_node_for_beacon(ic, ni,
22351df75eefSmaxv &ic->ic_channels[scan.sp_chan], scan.sp_ssid);
2236fdf8c16fSdyoung }
22370170309aSmaxv
223840e261aaSdyoung /*
223990634029Sdyoung * Count frame now that we know it's to be processed.
224040e261aaSdyoung */
224190634029Sdyoung if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
224211a42b5cSmaxv ic->ic_stats.is_rx_beacon++;
224390634029Sdyoung IEEE80211_NODE_STAT(ni, rx_beacons);
22440170309aSmaxv } else {
224590634029Sdyoung IEEE80211_NODE_STAT(ni, rx_proberesp);
22460170309aSmaxv }
224790634029Sdyoung
224890634029Sdyoung /*
224990634029Sdyoung * When operating in station mode, check for state updates.
225090634029Sdyoung * Be careful to ignore beacons received while doing a
225190634029Sdyoung * background scan. We consider only 11g/WMM stuff right now.
225290634029Sdyoung */
22530170309aSmaxv if (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd != 0 &&
225490634029Sdyoung ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
225590634029Sdyoung IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
225680b91eeaSdyoung /* record tsf of last beacon */
22571df75eefSmaxv memcpy(ni->ni_tstamp.data, scan.sp_tstamp, sizeof(ni->ni_tstamp));
22580170309aSmaxv
22591df75eefSmaxv if (ni->ni_erp != scan.sp_erp) {
226090634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
226190634029Sdyoung "[%s] erp change: was 0x%x, now 0x%x\n",
2262c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf),
22631df75eefSmaxv wh->i_addr2), ni->ni_erp, scan.sp_erp);
2264a45976c2Smaxv
226580b91eeaSdyoung if (ic->ic_curmode == IEEE80211_MODE_11G &&
2266a45976c2Smaxv (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) {
226790634029Sdyoung ic->ic_flags |= IEEE80211_F_USEPROT;
2268a45976c2Smaxv } else {
226990634029Sdyoung ic->ic_flags &= ~IEEE80211_F_USEPROT;
2270a45976c2Smaxv }
22711df75eefSmaxv ni->ni_erp = scan.sp_erp;
227290634029Sdyoung }
22730170309aSmaxv
22741df75eefSmaxv if ((ni->ni_capinfo ^ scan.sp_capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
227590634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
227690634029Sdyoung "[%s] capabilities change: before 0x%x,"
227790634029Sdyoung " now 0x%x\n",
2278c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf),
2279c2ec5838Schristos wh->i_addr2),
22801df75eefSmaxv ni->ni_capinfo, scan.sp_capinfo);
228190634029Sdyoung /*
228290634029Sdyoung * NB: we assume short preamble doesn't
228390634029Sdyoung * change dynamically
228490634029Sdyoung */
228590634029Sdyoung ieee80211_set_shortslottime(ic,
228690634029Sdyoung ic->ic_curmode == IEEE80211_MODE_11A ||
228790634029Sdyoung (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
22881df75eefSmaxv ni->ni_capinfo = scan.sp_capinfo;
228990634029Sdyoung }
22900170309aSmaxv
22911df75eefSmaxv if (scan.sp_wme != NULL && (ni->ni_flags & IEEE80211_NODE_QOS) &&
22921df75eefSmaxv ieee80211_parse_wmeparams(ic, scan.sp_wme, wh) > 0) {
229390634029Sdyoung ieee80211_wme_updateparams(ic);
22940170309aSmaxv }
22950170309aSmaxv
22961df75eefSmaxv if (scan.sp_tim != NULL) {
229780b91eeaSdyoung struct ieee80211_tim_ie *ie =
22981df75eefSmaxv (struct ieee80211_tim_ie *)scan.sp_tim;
229980b91eeaSdyoung
230080b91eeaSdyoung ni->ni_dtim_count = ie->tim_count;
230180b91eeaSdyoung ni->ni_dtim_period = ie->tim_period;
230280b91eeaSdyoung }
23030170309aSmaxv
23040170309aSmaxv if (ic->ic_flags & IEEE80211_F_SCAN) {
23050170309aSmaxv ieee80211_add_scan(ic, &scan, wh, subtype, rssi,
23060170309aSmaxv rstamp);
23070170309aSmaxv }
23080170309aSmaxv
230925e9e914Sdyoung ic->ic_bmiss_count = 0;
231090634029Sdyoung return;
231190634029Sdyoung }
231285acfa9cSmaxv
231390634029Sdyoung /*
231487515e34Sskrll * If scanning, just pass information to the scan module.
231590634029Sdyoung */
231687515e34Sskrll if (ic->ic_flags & IEEE80211_F_SCAN) {
23173fb751b1Stacha if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
23183fb751b1Stacha /*
23193fb751b1Stacha * Actively scanning a channel marked passive;
23203fb751b1Stacha * send a probe request now that we know there
23213fb751b1Stacha * is 802.11 traffic present.
23223fb751b1Stacha *
23233fb751b1Stacha * XXX check if the beacon we recv'd gives
23243fb751b1Stacha * us what we need and suppress the probe req
23253fb751b1Stacha */
23263fb751b1Stacha ieee80211_probe_curchan(ic, 1);
23273fb751b1Stacha ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
23283fb751b1Stacha }
23290170309aSmaxv ieee80211_add_scan(ic, &scan, wh, subtype, rssi, rstamp);
233090634029Sdyoung return;
233188a8480eSmycroft }
23320170309aSmaxv
23331df75eefSmaxv if (scan.sp_capinfo & IEEE80211_CAPINFO_IBSS) {
233485acfa9cSmaxv ieee80211_update_adhoc_node(ic, ni, wh, &scan, rssi, rstamp);
233540e261aaSdyoung }
23360170309aSmaxv }
233740e261aaSdyoung
233885acfa9cSmaxv static void
ieee80211_recv_mgmt_probe_req(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)233985acfa9cSmaxv ieee80211_recv_mgmt_probe_req(struct ieee80211com *ic, struct mbuf *m0,
234085acfa9cSmaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
234185acfa9cSmaxv {
234285acfa9cSmaxv struct ieee80211_frame *wh;
234385acfa9cSmaxv u_int8_t *frm, *efrm;
234485acfa9cSmaxv u_int8_t *ssid, *rates, *xrates;
2345a45976c2Smaxv bool need_free = false;
234685acfa9cSmaxv u_int8_t rate;
234785acfa9cSmaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
234885acfa9cSmaxv
234985acfa9cSmaxv wh = mtod(m0, struct ieee80211_frame *);
235085acfa9cSmaxv frm = (u_int8_t *)(wh + 1);
235185acfa9cSmaxv efrm = mtod(m0, u_int8_t *) + m0->m_len;
235285acfa9cSmaxv
235390634029Sdyoung if (ic->ic_opmode == IEEE80211_M_STA ||
235490634029Sdyoung ic->ic_state != IEEE80211_S_RUN) {
235590634029Sdyoung ic->ic_stats.is_rx_mgtdiscard++;
235640e261aaSdyoung return;
235790634029Sdyoung }
235890634029Sdyoung if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
235990634029Sdyoung /* frame must be directed */
2360a45976c2Smaxv ic->ic_stats.is_rx_mgtdiscard++;
236140e261aaSdyoung return;
236290634029Sdyoung }
236340e261aaSdyoung
236440e261aaSdyoung /*
236540e261aaSdyoung * prreq frame format
236640e261aaSdyoung * [tlv] ssid
236740e261aaSdyoung * [tlv] supported rates
236840e261aaSdyoung * [tlv] extended supported rates
236940e261aaSdyoung */
237040e261aaSdyoung ssid = rates = xrates = NULL;
2371a45976c2Smaxv while (frm + 1 < efrm) {
2372a45976c2Smaxv IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
2373a45976c2Smaxv
237440e261aaSdyoung switch (*frm) {
237540e261aaSdyoung case IEEE80211_ELEMID_SSID:
237640e261aaSdyoung ssid = frm;
237740e261aaSdyoung break;
237840e261aaSdyoung case IEEE80211_ELEMID_RATES:
237940e261aaSdyoung rates = frm;
238040e261aaSdyoung break;
238140e261aaSdyoung case IEEE80211_ELEMID_XRATES:
238240e261aaSdyoung xrates = frm;
238340e261aaSdyoung break;
238440e261aaSdyoung }
2385a45976c2Smaxv
238640e261aaSdyoung frm += frm[1] + 2;
238740e261aaSdyoung }
23880170309aSmaxv
238940e261aaSdyoung IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
239040e261aaSdyoung IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
239190634029Sdyoung IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
23920170309aSmaxv
239390634029Sdyoung if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
239490634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
239590634029Sdyoung wh, ieee80211_mgt_subtype_name[subtype >>
239690634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT],
239790634029Sdyoung "%s", "no ssid with ssid suppression enabled");
239890634029Sdyoung ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/
239990634029Sdyoung return;
240090634029Sdyoung }
240140e261aaSdyoung
240240e261aaSdyoung if (ni == ic->ic_bss) {
24030170309aSmaxv if (ic->ic_opmode != IEEE80211_M_IBSS) {
2404461cd4dbSdyoung ni = ieee80211_tmp_node(ic, wh->i_addr2);
2405a45976c2Smaxv need_free = true;
24060170309aSmaxv } else if (IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
2407461cd4dbSdyoung ;
24080170309aSmaxv } else {
240990634029Sdyoung /*
241090634029Sdyoung * XXX Cannot tell if the sender is operating
241190634029Sdyoung * in ibss mode. But we need a new node to
241290634029Sdyoung * send the response so blindly add them to the
241390634029Sdyoung * neighbor table.
241490634029Sdyoung */
241590634029Sdyoung ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
241690634029Sdyoung wh->i_addr2);
2417461cd4dbSdyoung }
24181d6dd12bSdyoung if (ni == NULL)
241940e261aaSdyoung return;
24200170309aSmaxv }
24210170309aSmaxv
24220170309aSmaxv IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, "[%s] recv probe req\n",
24230170309aSmaxv ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2));
24240170309aSmaxv
242540e261aaSdyoung ni->ni_rssi = rssi;
242640e261aaSdyoung ni->ni_rstamp = rstamp;
242787515e34Sskrll rate = ieee80211_setup_rates(ni, rates, xrates,
2428a45976c2Smaxv IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE |
2429a45976c2Smaxv IEEE80211_R_DONEGO | IEEE80211_R_DODEL);
24300170309aSmaxv
243140e261aaSdyoung if (rate & IEEE80211_RATE_BASIC) {
243290634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE,
243390634029Sdyoung wh, ieee80211_mgt_subtype_name[subtype >>
243490634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT],
243590634029Sdyoung "%s", "recv'd rate set invalid");
243640e261aaSdyoung } else {
243740e261aaSdyoung IEEE80211_SEND_MGMT(ic, ni,
243840e261aaSdyoung IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
243940e261aaSdyoung }
24400170309aSmaxv
2441a45976c2Smaxv if (need_free) {
244290634029Sdyoung /* reclaim immediately */
244390634029Sdyoung ieee80211_free_node(ni);
244490634029Sdyoung }
244585acfa9cSmaxv }
244685acfa9cSmaxv
2447f940273bSmaxv static void
ieee80211_recv_mgmt_auth(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)2448f940273bSmaxv ieee80211_recv_mgmt_auth(struct ieee80211com *ic, struct mbuf *m0,
244985acfa9cSmaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
245085acfa9cSmaxv {
245185acfa9cSmaxv struct ieee80211_frame *wh;
245285acfa9cSmaxv u_int8_t *frm, *efrm;
245385acfa9cSmaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
24540170309aSmaxv u_int16_t algo, seq, status;
245585acfa9cSmaxv
245685acfa9cSmaxv wh = mtod(m0, struct ieee80211_frame *);
245785acfa9cSmaxv frm = (u_int8_t *)(wh + 1);
245885acfa9cSmaxv efrm = mtod(m0, u_int8_t *) + m0->m_len;
245985acfa9cSmaxv
246040e261aaSdyoung /*
246140e261aaSdyoung * auth frame format
246240e261aaSdyoung * [2] algorithm
246340e261aaSdyoung * [2] sequence
246440e261aaSdyoung * [2] status
246540e261aaSdyoung * [tlv*] challenge
246640e261aaSdyoung */
246740e261aaSdyoung IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
246840e261aaSdyoung algo = le16toh(*(u_int16_t *)frm);
246940e261aaSdyoung seq = le16toh(*(u_int16_t *)(frm + 2));
247040e261aaSdyoung status = le16toh(*(u_int16_t *)(frm + 4));
24710170309aSmaxv
2472f0546001Smycroft IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
247390634029Sdyoung "[%s] recv auth frame with algorithm %d seq %d\n",
2474c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2), algo, seq);
24750170309aSmaxv
247690634029Sdyoung /*
247790634029Sdyoung * Consult the ACL policy module if setup.
247890634029Sdyoung */
24790170309aSmaxv if (ic->ic_acl != NULL && !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
248090634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL,
248190634029Sdyoung wh, "auth", "%s", "disallowed by ACL");
248290634029Sdyoung ic->ic_stats.is_rx_acl++;
248387515e34Sskrll if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
248487515e34Sskrll IEEE80211_SEND_MGMT(ic, ni,
248587515e34Sskrll IEEE80211_FC0_SUBTYPE_AUTH,
248687515e34Sskrll (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
248787515e34Sskrll }
248890634029Sdyoung return;
248990634029Sdyoung }
24900170309aSmaxv
249190634029Sdyoung if (ic->ic_flags & IEEE80211_F_COUNTERM) {
249290634029Sdyoung IEEE80211_DISCARD(ic,
249390634029Sdyoung IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
249490634029Sdyoung wh, "auth", "%s", "TKIP countermeasures enabled");
249590634029Sdyoung ic->ic_stats.is_rx_auth_countermeasures++;
249674988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
249790634029Sdyoung if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
249890634029Sdyoung IEEE80211_SEND_MGMT(ic, ni,
249990634029Sdyoung IEEE80211_FC0_SUBTYPE_AUTH,
250090634029Sdyoung IEEE80211_REASON_MIC_FAILURE);
250190634029Sdyoung }
25020170309aSmaxv #endif
250390634029Sdyoung return;
250490634029Sdyoung }
25050170309aSmaxv
2506a45976c2Smaxv if (algo == IEEE80211_AUTH_ALG_SHARED) {
2507db0e43b2Sdyoung ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
2508db0e43b2Sdyoung rstamp, seq, status);
2509a45976c2Smaxv } else if (algo == IEEE80211_AUTH_ALG_OPEN) {
2510a45976c2Smaxv ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, status);
2511a45976c2Smaxv } else {
251290634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
251390634029Sdyoung wh, "auth", "unsupported alg %d", algo);
25149280f4b4Sdyoung ic->ic_stats.is_rx_auth_unsupported++;
251574988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
251690634029Sdyoung if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
251790634029Sdyoung /* XXX not right */
251890634029Sdyoung IEEE80211_SEND_MGMT(ic, ni,
251990634029Sdyoung IEEE80211_FC0_SUBTYPE_AUTH,
252090634029Sdyoung (seq+1) | (IEEE80211_STATUS_ALG<<16));
252190634029Sdyoung }
25220170309aSmaxv #endif
252340e261aaSdyoung }
252440e261aaSdyoung }
252540e261aaSdyoung
25266d9e139fSmaxv static void
ieee80211_recv_mgmt_assoc_req(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)25276d9e139fSmaxv ieee80211_recv_mgmt_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
2528f940273bSmaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
2529f940273bSmaxv {
2530f940273bSmaxv struct ieee80211_frame *wh;
2531f940273bSmaxv u_int8_t *frm, *efrm;
2532f940273bSmaxv u_int8_t *ssid, *rates, *xrates, *wpa, *wme;
2533f940273bSmaxv int reassoc, resp;
2534f940273bSmaxv u_int8_t rate;
2535f940273bSmaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
25360170309aSmaxv u_int16_t capinfo, lintval;
25370170309aSmaxv struct ieee80211_rsnparms rsn;
25380170309aSmaxv u_int8_t reason;
2539f940273bSmaxv
2540f940273bSmaxv wh = mtod(m0, struct ieee80211_frame *);
2541f940273bSmaxv frm = (u_int8_t *)(wh + 1);
2542f940273bSmaxv efrm = mtod(m0, u_int8_t *) + m0->m_len;
2543f940273bSmaxv
254440e261aaSdyoung if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
254590634029Sdyoung ic->ic_state != IEEE80211_S_RUN) {
254690634029Sdyoung ic->ic_stats.is_rx_mgtdiscard++;
254740e261aaSdyoung return;
254890634029Sdyoung }
254940e261aaSdyoung
255040e261aaSdyoung if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
255140e261aaSdyoung reassoc = 1;
255240e261aaSdyoung resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
255340e261aaSdyoung } else {
255440e261aaSdyoung reassoc = 0;
255540e261aaSdyoung resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
255640e261aaSdyoung }
25570170309aSmaxv
255840e261aaSdyoung /*
255940e261aaSdyoung * asreq frame format
256040e261aaSdyoung * [2] capability information
256140e261aaSdyoung * [2] listen interval
256240e261aaSdyoung * [6*] current AP address (reassoc only)
256340e261aaSdyoung * [tlv] ssid
256440e261aaSdyoung * [tlv] supported rates
256540e261aaSdyoung * [tlv] extended supported rates
256690634029Sdyoung * [tlv] WPA or RSN
256740e261aaSdyoung */
256840e261aaSdyoung IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
2569a45976c2Smaxv
257040e261aaSdyoung if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
257190634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
257290634029Sdyoung wh, ieee80211_mgt_subtype_name[subtype >>
257390634029Sdyoung IEEE80211_FC0_SUBTYPE_SHIFT],
257490634029Sdyoung "%s", "wrong bssid");
25759280f4b4Sdyoung ic->ic_stats.is_rx_assoc_bss++;
257640e261aaSdyoung return;
257740e261aaSdyoung }
25780170309aSmaxv
257940e261aaSdyoung capinfo = le16toh(*(u_int16_t *)frm); frm += 2;
258087515e34Sskrll lintval = le16toh(*(u_int16_t *)frm); frm += 2;
258140e261aaSdyoung if (reassoc)
258240e261aaSdyoung frm += 6; /* ignore current AP info */
25830170309aSmaxv
258490634029Sdyoung ssid = rates = xrates = wpa = wme = NULL;
2585a45976c2Smaxv while (frm + 1 < efrm) {
2586a45976c2Smaxv IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
2587a45976c2Smaxv
258840e261aaSdyoung switch (*frm) {
258940e261aaSdyoung case IEEE80211_ELEMID_SSID:
259040e261aaSdyoung ssid = frm;
259140e261aaSdyoung break;
259240e261aaSdyoung case IEEE80211_ELEMID_RATES:
259340e261aaSdyoung rates = frm;
259440e261aaSdyoung break;
259540e261aaSdyoung case IEEE80211_ELEMID_XRATES:
259640e261aaSdyoung xrates = frm;
259740e261aaSdyoung break;
259890634029Sdyoung /* XXX verify only one of RSN and WPA ie's? */
259990634029Sdyoung case IEEE80211_ELEMID_RSN:
260090634029Sdyoung wpa = frm;
260190634029Sdyoung break;
260290634029Sdyoung case IEEE80211_ELEMID_VENDOR:
260387515e34Sskrll if (iswpaoui(frm))
260490634029Sdyoung wpa = frm;
260587515e34Sskrll else if (iswmeinfo(frm))
260690634029Sdyoung wme = frm;
260790634029Sdyoung /* XXX Atheros OUI support */
260890634029Sdyoung break;
260940e261aaSdyoung }
2610a45976c2Smaxv
261140e261aaSdyoung frm += frm[1] + 2;
261240e261aaSdyoung }
26130170309aSmaxv
261440e261aaSdyoung IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
261540e261aaSdyoung IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
261690634029Sdyoung IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
261775f8366dSmycroft
261890634029Sdyoung if (ni == ic->ic_bss) {
2619f0546001Smycroft IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
262090634029Sdyoung "[%s] deny %s request, sta not authenticated\n",
2621c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
262290634029Sdyoung reassoc ? "reassoc" : "assoc");
262380b91eeaSdyoung ieee80211_send_error(ic, ni, wh->i_addr2,
262440e261aaSdyoung IEEE80211_FC0_SUBTYPE_DEAUTH,
262540e261aaSdyoung IEEE80211_REASON_ASSOC_NOT_AUTHED);
26269280f4b4Sdyoung ic->ic_stats.is_rx_assoc_notauth++;
262740e261aaSdyoung return;
262840e261aaSdyoung }
26290170309aSmaxv
2630a45976c2Smaxv /* assert right association security credentials */
263190634029Sdyoung if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
263290634029Sdyoung IEEE80211_DPRINTF(ic,
263390634029Sdyoung IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
263490634029Sdyoung "[%s] no WPA/RSN IE in association request\n",
2635c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2));
2636a45976c2Smaxv
263790634029Sdyoung IEEE80211_SEND_MGMT(ic, ni,
263890634029Sdyoung IEEE80211_FC0_SUBTYPE_DEAUTH,
263990634029Sdyoung IEEE80211_REASON_RSN_REQUIRED);
264090634029Sdyoung ieee80211_node_leave(ic, ni);
2641a45976c2Smaxv
264290634029Sdyoung /* XXX distinguish WPA/RSN? */
264390634029Sdyoung ic->ic_stats.is_rx_assoc_badwpaie++;
264490634029Sdyoung return;
264590634029Sdyoung }
26460170309aSmaxv
264790634029Sdyoung if (wpa != NULL) {
264890634029Sdyoung /*
264990634029Sdyoung * Parse WPA information element. Note that
265090634029Sdyoung * we initialize the param block from the node
265190634029Sdyoung * state so that information in the IE overrides
265290634029Sdyoung * our defaults. The resulting parameters are
265390634029Sdyoung * installed below after the association is assured.
265490634029Sdyoung */
265590634029Sdyoung rsn = ni->ni_rsn;
265690634029Sdyoung if (wpa[0] != IEEE80211_ELEMID_RSN)
265790634029Sdyoung reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh);
265890634029Sdyoung else
265990634029Sdyoung reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh);
2660a45976c2Smaxv
266190634029Sdyoung if (reason != 0) {
266290634029Sdyoung IEEE80211_SEND_MGMT(ic, ni,
266390634029Sdyoung IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
266490634029Sdyoung ieee80211_node_leave(ic, ni);
2665a45976c2Smaxv
266690634029Sdyoung /* XXX distinguish WPA/RSN? */
266790634029Sdyoung ic->ic_stats.is_rx_assoc_badwpaie++;
266890634029Sdyoung return;
266990634029Sdyoung }
2670a45976c2Smaxv
267190634029Sdyoung IEEE80211_DPRINTF(ic,
267290634029Sdyoung IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
267390634029Sdyoung "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
2674c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
267590634029Sdyoung wpa[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN",
267690634029Sdyoung rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen,
267790634029Sdyoung rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen,
267890634029Sdyoung rsn.rsn_keymgmt, rsn.rsn_caps);
267990634029Sdyoung }
26800170309aSmaxv
2681db0e43b2Sdyoung /* discard challenge after association */
2682db0e43b2Sdyoung if (ni->ni_challenge != NULL) {
26839b87d582Scegger free(ni->ni_challenge, M_DEVBUF);
2684db0e43b2Sdyoung ni->ni_challenge = NULL;
2685db0e43b2Sdyoung }
26860170309aSmaxv
268780b91eeaSdyoung /* NB: 802.11 spec says to ignore station's privacy bit */
268880b91eeaSdyoung if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
2689f0546001Smycroft IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
269090634029Sdyoung "[%s] deny %s request, capability mismatch 0x%x\n",
2691c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
269290634029Sdyoung reassoc ? "reassoc" : "assoc", capinfo);
269340e261aaSdyoung IEEE80211_SEND_MGMT(ic, ni, resp,
269440e261aaSdyoung IEEE80211_STATUS_CAPINFO);
269535515831Smycroft ieee80211_node_leave(ic, ni);
26969280f4b4Sdyoung ic->ic_stats.is_rx_assoc_capmismatch++;
269740e261aaSdyoung return;
269840e261aaSdyoung }
26990170309aSmaxv
270087515e34Sskrll rate = ieee80211_setup_rates(ni, rates, xrates,
270197827674Schristos IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE |
270297827674Schristos IEEE80211_R_DONEGO | IEEE80211_R_DODEL);
27030170309aSmaxv
270480b91eeaSdyoung /*
270580b91eeaSdyoung * If constrained to 11g-only stations reject an
270680b91eeaSdyoung * 11b-only station. We cheat a bit here by looking
270780b91eeaSdyoung * at the max negotiated xmit rate and assuming anyone
270880b91eeaSdyoung * with a best rate <24Mb/s is an 11b station.
270980b91eeaSdyoung */
271080b91eeaSdyoung if ((rate & IEEE80211_RATE_BASIC) ||
271180b91eeaSdyoung ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48)) {
2712f0546001Smycroft IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
271390634029Sdyoung "[%s] deny %s request, rate set mismatch\n",
2714c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
271590634029Sdyoung reassoc ? "reassoc" : "assoc");
271640e261aaSdyoung IEEE80211_SEND_MGMT(ic, ni, resp,
271740e261aaSdyoung IEEE80211_STATUS_BASIC_RATE);
271835515831Smycroft ieee80211_node_leave(ic, ni);
27199280f4b4Sdyoung ic->ic_stats.is_rx_assoc_norate++;
272040e261aaSdyoung return;
272140e261aaSdyoung }
27220170309aSmaxv
272340e261aaSdyoung ni->ni_rssi = rssi;
272440e261aaSdyoung ni->ni_rstamp = rstamp;
272587515e34Sskrll ni->ni_intval = lintval;
272640e261aaSdyoung ni->ni_capinfo = capinfo;
272740e261aaSdyoung ni->ni_chan = ic->ic_bss->ni_chan;
272840e261aaSdyoung ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
272940e261aaSdyoung ni->ni_fhindex = ic->ic_bss->ni_fhindex;
27300170309aSmaxv
273190634029Sdyoung if (wpa != NULL) {
273290634029Sdyoung /*
273390634029Sdyoung * Record WPA/RSN parameters for station, mark
273490634029Sdyoung * node as using WPA and record information element
273590634029Sdyoung * for applications that require it.
273690634029Sdyoung */
273790634029Sdyoung ni->ni_rsn = rsn;
273890634029Sdyoung ieee80211_saveie(&ni->ni_wpa_ie, wpa);
273990634029Sdyoung } else if (ni->ni_wpa_ie != NULL) {
274090634029Sdyoung /*
274190634029Sdyoung * Flush any state from a previous association.
274290634029Sdyoung */
27439b87d582Scegger free(ni->ni_wpa_ie, M_DEVBUF);
274490634029Sdyoung ni->ni_wpa_ie = NULL;
274590634029Sdyoung }
27460170309aSmaxv
274790634029Sdyoung if (wme != NULL) {
274890634029Sdyoung /*
274990634029Sdyoung * Record WME parameters for station, mark node
275090634029Sdyoung * as capable of QoS and record information
275190634029Sdyoung * element for applications that require it.
275290634029Sdyoung */
275390634029Sdyoung ieee80211_saveie(&ni->ni_wme_ie, wme);
275490634029Sdyoung ni->ni_flags |= IEEE80211_NODE_QOS;
275590634029Sdyoung } else if (ni->ni_wme_ie != NULL) {
275690634029Sdyoung /*
275790634029Sdyoung * Flush any state from a previous association.
275890634029Sdyoung */
27599b87d582Scegger free(ni->ni_wme_ie, M_DEVBUF);
276090634029Sdyoung ni->ni_wme_ie = NULL;
276190634029Sdyoung ni->ni_flags &= ~IEEE80211_NODE_QOS;
276290634029Sdyoung }
27630170309aSmaxv
276435515831Smycroft ieee80211_node_join(ic, ni, resp);
276540e261aaSdyoung }
276640e261aaSdyoung
276724023c75Smaxv #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
27686d9e139fSmaxv
276924023c75Smaxv static void
ieee80211_recv_mgmt_assoc_resp(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)277024023c75Smaxv ieee80211_recv_mgmt_assoc_resp(struct ieee80211com *ic, struct mbuf *m0,
27716d9e139fSmaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
27726d9e139fSmaxv {
27736d9e139fSmaxv struct ieee80211_frame *wh;
27746d9e139fSmaxv u_int8_t *frm, *efrm;
2775a45976c2Smaxv u_int8_t *rates, *xrates, *wme;
27766d9e139fSmaxv u_int8_t rate;
27776d9e139fSmaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
27780170309aSmaxv u_int16_t capinfo, associd;
27790170309aSmaxv u_int16_t status;
27806d9e139fSmaxv
27816d9e139fSmaxv wh = mtod(m0, struct ieee80211_frame *);
27826d9e139fSmaxv frm = (u_int8_t *)(wh + 1);
27836d9e139fSmaxv efrm = mtod(m0, u_int8_t *) + m0->m_len;
27846d9e139fSmaxv
278540e261aaSdyoung if (ic->ic_opmode != IEEE80211_M_STA ||
2786a02b46e8Smycroft ic->ic_state != IEEE80211_S_ASSOC) {
2787a02b46e8Smycroft ic->ic_stats.is_rx_mgtdiscard++;
278840e261aaSdyoung return;
2789a02b46e8Smycroft }
279040e261aaSdyoung
279140e261aaSdyoung /*
279240e261aaSdyoung * asresp frame format
279340e261aaSdyoung * [2] capability information
279440e261aaSdyoung * [2] status
279540e261aaSdyoung * [2] association ID
279640e261aaSdyoung * [tlv] supported rates
279740e261aaSdyoung * [tlv] extended supported rates
279890634029Sdyoung * [tlv] WME
279940e261aaSdyoung */
280040e261aaSdyoung IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
280140e261aaSdyoung ni = ic->ic_bss;
280290634029Sdyoung capinfo = le16toh(*(u_int16_t *)frm);
280340e261aaSdyoung frm += 2;
280440e261aaSdyoung status = le16toh(*(u_int16_t *)frm);
280540e261aaSdyoung frm += 2;
280640e261aaSdyoung if (status != 0) {
2807a02b46e8Smycroft IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
280890634029Sdyoung "[%s] %sassoc failed (reason %d)\n",
2809c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
281090634029Sdyoung ISREASSOC(subtype) ? "re" : "", status);
281190634029Sdyoung if (ni != ic->ic_bss) /* XXX never true? */
281240e261aaSdyoung ni->ni_fails++;
281390634029Sdyoung ic->ic_stats.is_rx_auth_fail++; /* XXX */
281440e261aaSdyoung return;
281540e261aaSdyoung }
281690634029Sdyoung associd = le16toh(*(u_int16_t *)frm);
281740e261aaSdyoung frm += 2;
281840e261aaSdyoung
2819a45976c2Smaxv rates = xrates = wme = NULL;
2820a45976c2Smaxv while (frm + 1 < efrm) {
2821a45976c2Smaxv IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2);
2822a45976c2Smaxv
282340e261aaSdyoung switch (*frm) {
282440e261aaSdyoung case IEEE80211_ELEMID_RATES:
282540e261aaSdyoung rates = frm;
282640e261aaSdyoung break;
282740e261aaSdyoung case IEEE80211_ELEMID_XRATES:
282840e261aaSdyoung xrates = frm;
282940e261aaSdyoung break;
283090634029Sdyoung case IEEE80211_ELEMID_VENDOR:
283190634029Sdyoung if (iswmeoui(frm))
283290634029Sdyoung wme = frm;
283390634029Sdyoung /* XXX Atheros OUI support */
283490634029Sdyoung break;
283540e261aaSdyoung }
2836a45976c2Smaxv
283740e261aaSdyoung frm += frm[1] + 2;
283840e261aaSdyoung }
283940e261aaSdyoung
284040e261aaSdyoung IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
2841a45976c2Smaxv
284287515e34Sskrll rate = ieee80211_setup_rates(ni, rates, xrates,
284397827674Schristos IEEE80211_R_DOSORT | IEEE80211_R_DOFRATE |
284497827674Schristos IEEE80211_R_DONEGO | IEEE80211_R_DODEL);
2845a45976c2Smaxv
284680b91eeaSdyoung if (rate & IEEE80211_RATE_BASIC) {
284790634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
284890634029Sdyoung "[%s] %sassoc failed (rate set mismatch)\n",
2849c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
285090634029Sdyoung ISREASSOC(subtype) ? "re" : "");
285190634029Sdyoung if (ni != ic->ic_bss) /* XXX never true? */
285290634029Sdyoung ni->ni_fails++;
285390634029Sdyoung ic->ic_stats.is_rx_assoc_norate++;
285480b91eeaSdyoung ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
285590634029Sdyoung return;
285690634029Sdyoung }
285790634029Sdyoung
285890634029Sdyoung ni->ni_capinfo = capinfo;
285990634029Sdyoung ni->ni_associd = associd;
2860a45976c2Smaxv if (wme != NULL && ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
286190634029Sdyoung ni->ni_flags |= IEEE80211_NODE_QOS;
286290634029Sdyoung ieee80211_wme_updateparams(ic);
28630170309aSmaxv } else {
286490634029Sdyoung ni->ni_flags &= ~IEEE80211_NODE_QOS;
28650170309aSmaxv }
28660170309aSmaxv
286790634029Sdyoung /*
286890634029Sdyoung * Configure state now that we are associated.
286990634029Sdyoung *
287090634029Sdyoung * XXX may need different/additional driver callbacks?
287190634029Sdyoung */
287290634029Sdyoung if (ic->ic_curmode == IEEE80211_MODE_11A ||
287390634029Sdyoung (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
287490634029Sdyoung ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
287590634029Sdyoung ic->ic_flags &= ~IEEE80211_F_USEBARKER;
287690634029Sdyoung } else {
287790634029Sdyoung ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
287890634029Sdyoung ic->ic_flags |= IEEE80211_F_USEBARKER;
287990634029Sdyoung }
288090634029Sdyoung ieee80211_set_shortslottime(ic,
288190634029Sdyoung ic->ic_curmode == IEEE80211_MODE_11A ||
288290634029Sdyoung (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
28830170309aSmaxv
288490634029Sdyoung /*
288590634029Sdyoung * Honor ERP protection.
288690634029Sdyoung *
288790634029Sdyoung * NB: ni_erp should zero for non-11g operation.
288890634029Sdyoung * XXX check ic_curmode anyway?
288990634029Sdyoung */
289080b91eeaSdyoung if (ic->ic_curmode == IEEE80211_MODE_11G &&
2891a45976c2Smaxv (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) {
289290634029Sdyoung ic->ic_flags |= IEEE80211_F_USEPROT;
2893a45976c2Smaxv } else {
289490634029Sdyoung ic->ic_flags &= ~IEEE80211_F_USEPROT;
2895a45976c2Smaxv }
28960170309aSmaxv
289790634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
289890634029Sdyoung "[%s] %sassoc success: %s preamble, %s slot time%s%s\n",
2899c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2),
290090634029Sdyoung ISREASSOC(subtype) ? "re" : "",
290190634029Sdyoung ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
290290634029Sdyoung ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
290390634029Sdyoung ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
290490634029Sdyoung ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : ""
290590634029Sdyoung );
29060170309aSmaxv
290790634029Sdyoung ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
290840e261aaSdyoung }
290940e261aaSdyoung
2910e51554e1Smaxv static void
ieee80211_recv_mgmt_deauth(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)2911e51554e1Smaxv ieee80211_recv_mgmt_deauth(struct ieee80211com *ic, struct mbuf *m0,
2912e51554e1Smaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
2913e51554e1Smaxv {
2914e51554e1Smaxv struct ieee80211_frame *wh;
2915e51554e1Smaxv u_int8_t *frm, *efrm;
2916e51554e1Smaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
29170170309aSmaxv u_int16_t reason;
2918e51554e1Smaxv
2919e51554e1Smaxv wh = mtod(m0, struct ieee80211_frame *);
2920e51554e1Smaxv frm = (u_int8_t *)(wh + 1);
2921e51554e1Smaxv efrm = mtod(m0, u_int8_t *) + m0->m_len;
2922e51554e1Smaxv
2923e51554e1Smaxv if (ic->ic_state == IEEE80211_S_SCAN) {
2924e51554e1Smaxv ic->ic_stats.is_rx_mgtdiscard++;
2925e51554e1Smaxv return;
2926e51554e1Smaxv }
29270170309aSmaxv
2928e51554e1Smaxv /*
2929e51554e1Smaxv * deauth frame format
2930e51554e1Smaxv * [2] reason
2931e51554e1Smaxv */
2932e51554e1Smaxv IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
2933e51554e1Smaxv reason = le16toh(*(u_int16_t *)frm);
2934e51554e1Smaxv __USE(reason);
2935e51554e1Smaxv ic->ic_stats.is_rx_deauth++;
2936e51554e1Smaxv IEEE80211_NODE_STAT(ni, rx_deauth);
2937e51554e1Smaxv
2938e51554e1Smaxv if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
2939e51554e1Smaxv /* Not intended for this station. */
2940e51554e1Smaxv ic->ic_stats.is_rx_mgtdiscard++;
2941e51554e1Smaxv return;
2942e51554e1Smaxv }
29430170309aSmaxv
2944e51554e1Smaxv IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
2945e51554e1Smaxv "[%s] recv deauthenticate (reason %d)\n",
2946e51554e1Smaxv ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), reason);
29470170309aSmaxv
2948e51554e1Smaxv switch (ic->ic_opmode) {
2949e51554e1Smaxv case IEEE80211_M_STA:
2950e51554e1Smaxv ieee80211_new_state(ic, IEEE80211_S_AUTH,
2951e51554e1Smaxv wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
2952e51554e1Smaxv break;
2953e51554e1Smaxv case IEEE80211_M_HOSTAP:
2954e51554e1Smaxv #ifndef IEEE80211_NO_HOSTAP
2955e51554e1Smaxv if (ni != ic->ic_bss)
2956e51554e1Smaxv ieee80211_node_leave(ic, ni);
29570170309aSmaxv #endif
2958e51554e1Smaxv break;
2959e51554e1Smaxv default:
2960e51554e1Smaxv ic->ic_stats.is_rx_mgtdiscard++;
2961e51554e1Smaxv break;
2962e51554e1Smaxv }
2963e51554e1Smaxv }
2964e51554e1Smaxv
296582e96ed7Smaxv static void
ieee80211_recv_mgmt_disassoc(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)296682e96ed7Smaxv ieee80211_recv_mgmt_disassoc(struct ieee80211com *ic, struct mbuf *m0,
296724023c75Smaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
296824023c75Smaxv {
296924023c75Smaxv struct ieee80211_frame *wh;
297024023c75Smaxv u_int8_t *frm, *efrm;
297124023c75Smaxv IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
29720170309aSmaxv u_int16_t reason;
297324023c75Smaxv
297424023c75Smaxv wh = mtod(m0, struct ieee80211_frame *);
297524023c75Smaxv frm = (u_int8_t *)(wh + 1);
297624023c75Smaxv efrm = mtod(m0, u_int8_t *) + m0->m_len;
297724023c75Smaxv
297882e96ed7Smaxv if (ic->ic_state != IEEE80211_S_RUN &&
297982e96ed7Smaxv ic->ic_state != IEEE80211_S_ASSOC &&
298082e96ed7Smaxv ic->ic_state != IEEE80211_S_AUTH) {
298182e96ed7Smaxv ic->ic_stats.is_rx_mgtdiscard++;
298282e96ed7Smaxv return;
298382e96ed7Smaxv }
29840170309aSmaxv
298582e96ed7Smaxv /*
298682e96ed7Smaxv * disassoc frame format
298782e96ed7Smaxv * [2] reason
298882e96ed7Smaxv */
298982e96ed7Smaxv IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
299082e96ed7Smaxv reason = le16toh(*(u_int16_t *)frm);
299182e96ed7Smaxv __USE(reason);
299282e96ed7Smaxv ic->ic_stats.is_rx_disassoc++;
299382e96ed7Smaxv IEEE80211_NODE_STAT(ni, rx_disassoc);
299482e96ed7Smaxv
299582e96ed7Smaxv if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
299682e96ed7Smaxv /* Not intended for this station. */
299782e96ed7Smaxv ic->ic_stats.is_rx_mgtdiscard++;
299882e96ed7Smaxv return;
299982e96ed7Smaxv }
30000170309aSmaxv
300182e96ed7Smaxv IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
300282e96ed7Smaxv "[%s] recv disassociate (reason %d)\n",
300382e96ed7Smaxv ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), reason);
30040170309aSmaxv
300582e96ed7Smaxv switch (ic->ic_opmode) {
300682e96ed7Smaxv case IEEE80211_M_STA:
300782e96ed7Smaxv ieee80211_new_state(ic, IEEE80211_S_ASSOC,
300882e96ed7Smaxv wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
300982e96ed7Smaxv break;
301082e96ed7Smaxv case IEEE80211_M_HOSTAP:
301182e96ed7Smaxv #ifndef IEEE80211_NO_HOSTAP
301282e96ed7Smaxv if (ni != ic->ic_bss)
301382e96ed7Smaxv ieee80211_node_leave(ic, ni);
30140170309aSmaxv #endif
301582e96ed7Smaxv break;
301682e96ed7Smaxv default:
301782e96ed7Smaxv ic->ic_stats.is_rx_mgtdiscard++;
301882e96ed7Smaxv break;
301982e96ed7Smaxv }
302082e96ed7Smaxv }
302182e96ed7Smaxv
30221551d983Smaxv #undef ISREASSOC
30231551d983Smaxv #undef IEEE80211_VERIFY_LENGTH
30241551d983Smaxv #undef IEEE80211_VERIFY_ELEMENT
30251551d983Smaxv
302682e96ed7Smaxv /* -------------------------------------------------------------------------- */
302782e96ed7Smaxv
302882e96ed7Smaxv void
ieee80211_recv_mgmt(struct ieee80211com * ic,struct mbuf * m0,struct ieee80211_node * ni,int subtype,int rssi,u_int32_t rstamp)302982e96ed7Smaxv ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
303082e96ed7Smaxv struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
303182e96ed7Smaxv {
303282e96ed7Smaxv struct ieee80211_frame *wh;
303382e96ed7Smaxv
303482e96ed7Smaxv wh = mtod(m0, struct ieee80211_frame *);
303582e96ed7Smaxv
303624023c75Smaxv switch (subtype) {
303724023c75Smaxv case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
303824023c75Smaxv case IEEE80211_FC0_SUBTYPE_BEACON:
303924023c75Smaxv ieee80211_recv_mgmt_beacon(ic, m0, ni, subtype, rssi, rstamp);
304024023c75Smaxv return;
304124023c75Smaxv
304224023c75Smaxv case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
304324023c75Smaxv ieee80211_recv_mgmt_probe_req(ic, m0, ni, subtype, rssi, rstamp);
304424023c75Smaxv return;
304524023c75Smaxv
304624023c75Smaxv case IEEE80211_FC0_SUBTYPE_AUTH:
304724023c75Smaxv ieee80211_recv_mgmt_auth(ic, m0, ni, subtype, rssi, rstamp);
304824023c75Smaxv return;
304924023c75Smaxv
305024023c75Smaxv case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
305124023c75Smaxv case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
305224023c75Smaxv ieee80211_recv_mgmt_assoc_req(ic, m0, ni, subtype, rssi, rstamp);
305324023c75Smaxv return;
305424023c75Smaxv
305524023c75Smaxv case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
305624023c75Smaxv case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
305724023c75Smaxv ieee80211_recv_mgmt_assoc_resp(ic, m0, ni, subtype, rssi, rstamp);
305824023c75Smaxv return;
305924023c75Smaxv
3060e51554e1Smaxv case IEEE80211_FC0_SUBTYPE_DEAUTH:
3061e51554e1Smaxv ieee80211_recv_mgmt_deauth(ic, m0, ni, subtype, rssi, rstamp);
306290634029Sdyoung return;
306340e261aaSdyoung
306482e96ed7Smaxv case IEEE80211_FC0_SUBTYPE_DISASSOC:
306582e96ed7Smaxv ieee80211_recv_mgmt_disassoc(ic, m0, ni, subtype, rssi, rstamp);
306690634029Sdyoung return;
306780b91eeaSdyoung
306840e261aaSdyoung default:
306990634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
307090634029Sdyoung wh, "mgt", "subtype 0x%x not handled", subtype);
30719280f4b4Sdyoung ic->ic_stats.is_rx_badsubtype++;
307240e261aaSdyoung break;
307340e261aaSdyoung }
307490634029Sdyoung }
307582e96ed7Smaxv
307674988b0fSdyoung #ifndef IEEE80211_NO_HOSTAP
307790634029Sdyoung /*
307890634029Sdyoung * Handle station power-save state change.
307990634029Sdyoung */
308090634029Sdyoung static void
ieee80211_node_pwrsave(struct ieee80211_node * ni,int enable)308190634029Sdyoung ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
308290634029Sdyoung {
308390634029Sdyoung struct ieee80211com *ic = ni->ni_ic;
308490634029Sdyoung struct mbuf *m;
3085c2ec5838Schristos IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
308690634029Sdyoung
308790634029Sdyoung if (enable) {
308890634029Sdyoung if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
308990634029Sdyoung ic->ic_ps_sta++;
309090634029Sdyoung ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
309190634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
309290634029Sdyoung "[%s] power save mode on, %u sta's in ps mode\n",
3093c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
3094c2ec5838Schristos ic->ic_ps_sta);
309590634029Sdyoung return;
309640e261aaSdyoung }
3097b8e804e8Sdyoung
309890634029Sdyoung if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
309990634029Sdyoung ic->ic_ps_sta--;
310090634029Sdyoung ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
310190634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
310290634029Sdyoung "[%s] power save mode off, %u sta's in ps mode\n",
3103c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), ic->ic_ps_sta);
310490634029Sdyoung /* XXX if no stations in ps mode, flush mc frames */
310590634029Sdyoung
310690634029Sdyoung /*
310790634029Sdyoung * Flush queued unicast frames.
310890634029Sdyoung */
310990634029Sdyoung if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
311080b91eeaSdyoung if (ic->ic_set_tim != NULL)
311187515e34Sskrll ic->ic_set_tim(ni, 0); /* just in case */
311290634029Sdyoung return;
311390634029Sdyoung }
3114a45976c2Smaxv
311590634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
311690634029Sdyoung "[%s] flush ps queue, %u packets queued\n",
3117c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr),
3118c2ec5838Schristos IEEE80211_NODE_SAVEQ_QLEN(ni));
3119a45976c2Smaxv
312090634029Sdyoung for (;;) {
312190634029Sdyoung int qlen;
312290634029Sdyoung
312390634029Sdyoung IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
312490634029Sdyoung if (m == NULL)
312590634029Sdyoung break;
3126a45976c2Smaxv
312790634029Sdyoung /*
312890634029Sdyoung * If this is the last packet, turn off the TIM bit.
312990634029Sdyoung * If there are more packets, set the more packets bit
313080b91eeaSdyoung * in the mbuf so ieee80211_encap will mark the 802.11
313180b91eeaSdyoung * head to indicate more data frames will follow.
313290634029Sdyoung */
313380b91eeaSdyoung if (qlen != 0)
313480b91eeaSdyoung m->m_flags |= M_MORE_DATA;
3135a45976c2Smaxv
313690634029Sdyoung /* XXX need different driver interface */
313790634029Sdyoung /* XXX bypasses q max */
313890634029Sdyoung IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
313990634029Sdyoung }
3140a45976c2Smaxv
314180b91eeaSdyoung if (ic->ic_set_tim != NULL)
314287515e34Sskrll ic->ic_set_tim(ni, 0);
314390634029Sdyoung }
314490634029Sdyoung
314590634029Sdyoung /*
314690634029Sdyoung * Process a received ps-poll frame.
314790634029Sdyoung */
3148b8e804e8Sdyoung static void
ieee80211_recv_pspoll(struct ieee80211com * ic,struct ieee80211_node * ni,struct mbuf * m0)3149a45976c2Smaxv ieee80211_recv_pspoll(struct ieee80211com *ic, struct ieee80211_node *ni,
3150a45976c2Smaxv struct mbuf *m0)
3151b8e804e8Sdyoung {
315290634029Sdyoung struct ieee80211_frame_min *wh;
3153b8e804e8Sdyoung struct mbuf *m;
3154b8e804e8Sdyoung u_int16_t aid;
315590634029Sdyoung int qlen;
3156c2ec5838Schristos IEEE80211_DEBUGVAR(char ebuf[3 * ETHER_ADDR_LEN]);
3157b8e804e8Sdyoung
315890634029Sdyoung wh = mtod(m0, struct ieee80211_frame_min *);
315990634029Sdyoung if (ni->ni_associd == 0) {
316090634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
316190634029Sdyoung (struct ieee80211_frame *)wh, "ps-poll",
316290634029Sdyoung "%s", "unassociated station");
316390634029Sdyoung ic->ic_stats.is_ps_unassoc++;
316490634029Sdyoung IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
316590634029Sdyoung IEEE80211_REASON_NOT_ASSOCED);
3166b8e804e8Sdyoung return;
3167b8e804e8Sdyoung }
3168b8e804e8Sdyoung
316990634029Sdyoung aid = le16toh(*(u_int16_t *)wh->i_dur);
3170b8e804e8Sdyoung if (aid != ni->ni_associd) {
317190634029Sdyoung IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
317290634029Sdyoung (struct ieee80211_frame *)wh, "ps-poll",
317390634029Sdyoung "aid mismatch: sta aid 0x%x poll aid 0x%x",
3174b8e804e8Sdyoung ni->ni_associd, aid);
317590634029Sdyoung ic->ic_stats.is_ps_badaid++;
317690634029Sdyoung IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
317790634029Sdyoung IEEE80211_REASON_NOT_ASSOCED);
3178b8e804e8Sdyoung return;
3179b8e804e8Sdyoung }
3180b8e804e8Sdyoung
3181b8e804e8Sdyoung /* Okay, take the first queued packet and put it out... */
318290634029Sdyoung IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
3183b8e804e8Sdyoung if (m == NULL) {
318490634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
318590634029Sdyoung "[%s] recv ps-poll, but queue empty\n",
3186c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), wh->i_addr2));
318787515e34Sskrll ieee80211_send_nulldata(ieee80211_ref_node(ni));
318890634029Sdyoung ic->ic_stats.is_ps_qempty++; /* XXX node stat */
318980b91eeaSdyoung if (ic->ic_set_tim != NULL)
319087515e34Sskrll ic->ic_set_tim(ni, 0); /* just in case */
3191b8e804e8Sdyoung return;
3192b8e804e8Sdyoung }
3193a45976c2Smaxv
319490634029Sdyoung /*
319590634029Sdyoung * If there are more packets, set the more packets bit
319690634029Sdyoung * in the packet dispatched to the station; otherwise
319790634029Sdyoung * turn off the TIM bit.
319890634029Sdyoung */
319990634029Sdyoung if (qlen != 0) {
320090634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
320190634029Sdyoung "[%s] recv ps-poll, send packet, %u still queued\n",
3202c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr), qlen);
320380b91eeaSdyoung m->m_flags |= M_MORE_DATA;
320490634029Sdyoung } else {
320590634029Sdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
320690634029Sdyoung "[%s] recv ps-poll, send packet, queue empty\n",
3207c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ni->ni_macaddr));
320880b91eeaSdyoung if (ic->ic_set_tim != NULL)
320987515e34Sskrll ic->ic_set_tim(ni, 0);
321090634029Sdyoung }
3211a45976c2Smaxv
321290634029Sdyoung m->m_flags |= M_PWR_SAV; /* bypass PS handling */
321390634029Sdyoung IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
321490634029Sdyoung }
321574988b0fSdyoung #endif /* !IEEE80211_NO_HOSTAP */
321690634029Sdyoung
321790634029Sdyoung #ifdef IEEE80211_DEBUG
321890634029Sdyoung /*
321990634029Sdyoung * Debugging support.
322090634029Sdyoung */
3221b8e804e8Sdyoung
3222b8e804e8Sdyoung /*
322390634029Sdyoung * Return the bssid of a frame.
3224b8e804e8Sdyoung */
322590634029Sdyoung static const u_int8_t *
ieee80211_getbssid(struct ieee80211com * ic,const struct ieee80211_frame * wh)322690634029Sdyoung ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
3227ce488d2bSdyoung {
322890634029Sdyoung if (ic->ic_opmode == IEEE80211_M_STA)
322990634029Sdyoung return wh->i_addr2;
323090634029Sdyoung if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
323190634029Sdyoung return wh->i_addr1;
323290634029Sdyoung if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
323390634029Sdyoung return wh->i_addr1;
323490634029Sdyoung return wh->i_addr3;
3235ce488d2bSdyoung }
3236ce488d2bSdyoung
323787515e34Sskrll void
ieee80211_note(struct ieee80211com * ic,const char * fmt,...)323887515e34Sskrll ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
323987515e34Sskrll {
324087515e34Sskrll char buf[128]; /* XXX */
324187515e34Sskrll va_list ap;
324287515e34Sskrll
324387515e34Sskrll va_start(ap, fmt);
324487515e34Sskrll vsnprintf(buf, sizeof(buf), fmt, ap);
324587515e34Sskrll va_end(ap);
324687515e34Sskrll
324787515e34Sskrll if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */
324887515e34Sskrll }
324987515e34Sskrll
325087515e34Sskrll void
ieee80211_note_mac(struct ieee80211com * ic,const u_int8_t mac[IEEE80211_ADDR_LEN],const char * fmt,...)325187515e34Sskrll ieee80211_note_mac(struct ieee80211com *ic,
325287515e34Sskrll const u_int8_t mac[IEEE80211_ADDR_LEN],
325387515e34Sskrll const char *fmt, ...)
325487515e34Sskrll {
325587515e34Sskrll char buf[128]; /* XXX */
325687515e34Sskrll va_list ap;
3257c2ec5838Schristos char ebuf[3 * ETHER_ADDR_LEN];
325887515e34Sskrll
325987515e34Sskrll va_start(ap, fmt);
326087515e34Sskrll vsnprintf(buf, sizeof(buf), fmt, ap);
326187515e34Sskrll va_end(ap);
3262c2ec5838Schristos if_printf(ic->ic_ifp, "[%s] %s\n", ether_snprintf(ebuf, sizeof(ebuf),
3263c2ec5838Schristos mac), buf);
326487515e34Sskrll }
326587515e34Sskrll
326690634029Sdyoung static void
ieee80211_discard_frame(struct ieee80211com * ic,const struct ieee80211_frame * wh,const char * type,const char * fmt,...)326790634029Sdyoung ieee80211_discard_frame(struct ieee80211com *ic,
326890634029Sdyoung const struct ieee80211_frame *wh,
326990634029Sdyoung const char *type, const char *fmt, ...)
3270ce488d2bSdyoung {
327190634029Sdyoung va_list ap;
3272c2ec5838Schristos char ebuf[3 * ETHER_ADDR_LEN];
3273ce488d2bSdyoung
327487515e34Sskrll printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
3275c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ieee80211_getbssid(ic, wh)));
327690634029Sdyoung if (type != NULL)
327790634029Sdyoung printf("%s frame, ", type);
327890634029Sdyoung else
327990634029Sdyoung printf("frame, ");
328090634029Sdyoung va_start(ap, fmt);
328190634029Sdyoung vprintf(fmt, ap);
328290634029Sdyoung va_end(ap);
328390634029Sdyoung printf("\n");
3284ce488d2bSdyoung }
3285ce488d2bSdyoung
328690634029Sdyoung static void
ieee80211_discard_ie(struct ieee80211com * ic,const struct ieee80211_frame * wh,const char * type,const char * fmt,...)328790634029Sdyoung ieee80211_discard_ie(struct ieee80211com *ic,
328890634029Sdyoung const struct ieee80211_frame *wh,
328990634029Sdyoung const char *type, const char *fmt, ...)
329090634029Sdyoung {
329190634029Sdyoung va_list ap;
3292c2ec5838Schristos char ebuf[3 * ETHER_ADDR_LEN];
3293ce488d2bSdyoung
329487515e34Sskrll printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
3295c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), ieee80211_getbssid(ic, wh)));
329690634029Sdyoung if (type != NULL)
329790634029Sdyoung printf("%s information element, ", type);
329890634029Sdyoung else
329990634029Sdyoung printf("information element, ");
330090634029Sdyoung va_start(ap, fmt);
330190634029Sdyoung vprintf(fmt, ap);
330290634029Sdyoung va_end(ap);
330390634029Sdyoung printf("\n");
3304ce488d2bSdyoung }
330590634029Sdyoung
330690634029Sdyoung static void
ieee80211_discard_mac(struct ieee80211com * ic,const u_int8_t mac[IEEE80211_ADDR_LEN],const char * type,const char * fmt,...)330790634029Sdyoung ieee80211_discard_mac(struct ieee80211com *ic,
330890634029Sdyoung const u_int8_t mac[IEEE80211_ADDR_LEN],
330990634029Sdyoung const char *type, const char *fmt, ...)
331090634029Sdyoung {
331190634029Sdyoung va_list ap;
3312c2ec5838Schristos char ebuf[3 * ETHER_ADDR_LEN];
331390634029Sdyoung
3314c2ec5838Schristos printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
3315c2ec5838Schristos ether_snprintf(ebuf, sizeof(ebuf), mac));
331690634029Sdyoung if (type != NULL)
331790634029Sdyoung printf("%s frame, ", type);
331890634029Sdyoung else
331990634029Sdyoung printf("frame, ");
332090634029Sdyoung va_start(ap, fmt);
332190634029Sdyoung vprintf(fmt, ap);
332290634029Sdyoung va_end(ap);
332390634029Sdyoung printf("\n");
332490634029Sdyoung }
332590634029Sdyoung #endif /* IEEE80211_DEBUG */
3326