diff options
Diffstat (limited to 'sys/net80211/ieee80211_output.c')
-rw-r--r-- | sys/net80211/ieee80211_output.c | 2026 |
1 files changed, 1288 insertions, 738 deletions
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 6e31d1d..cc2a911 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2001 Atsushi Onoe - * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" +#include "opt_wlan.h" #include <sys/param.h> #include <sys/systm.h> @@ -46,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_wds.h> #ifdef INET #include <netinet/in.h> @@ -57,10 +59,10 @@ __FBSDID("$FreeBSD$"); #define ETHER_HEADER_COPY(dst, src) \ memcpy(dst, src, sizeof(struct ether_header)) -static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic, +static struct mbuf *ieee80211_encap_fastframe(struct ieee80211vap *, struct mbuf *m1, const struct ether_header *eh1, struct mbuf *m2, const struct ether_header *eh2); -static int ieee80211_fragment(struct ieee80211com *, struct mbuf *, +static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *, u_int hdrsize, u_int ciphdrsize, u_int mtu); static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); @@ -72,24 +74,344 @@ static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); * (e.g. beacons). */ static __inline int -doprint(struct ieee80211com *ic, int subtype) +doprint(struct ieee80211vap *vap, int subtype) { switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - return (ic->ic_opmode == IEEE80211_M_IBSS); + return (vap->iv_opmode == IEEE80211_M_IBSS); } return 1; } #endif /* + * Start method for vap's. All packets from the stack come + * through here. We handle common processing of the packets + * before dispatching them to the underlying device. + */ +void +ieee80211_start(struct ifnet *ifp) +{ +#define IS_DWDS(vap) \ + (vap->iv_opmode == IEEE80211_M_WDS && \ + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *parent = ic->ic_ifp; + struct ieee80211_node *ni; + struct mbuf *m; + struct ether_header *eh; + int error; + + /* NB: parent must be up and running */ + if (!IFNET_IS_UP_RUNNING(parent)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: ignore queue, parent %s not up+running\n", + __func__, parent->if_xname); + /* XXX stat */ + return; + } + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* + * In power save, wakeup device for transmit. + */ + ieee80211_new_state(vap, IEEE80211_S_RUN, 0); + return; + } + /* + * No data frames go out unless we're running. + * Note in particular this covers CAC and CSA + * states (though maybe we should check muting + * for CSA). + */ + if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_LOCK(ic); + /* re-check under the com lock to avoid races */ + if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: ignore queue, in %s state\n", + __func__, ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_tx_badstate++; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IEEE80211_UNLOCK(ic); + return; + } + IEEE80211_UNLOCK(ic); + } + for (;;) { + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + /* + * Sanitize mbuf flags for net80211 use. We cannot + * clear M_PWR_SAV because this may be set for frames + * that are re-submitted from the power save queue. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~(M_80211_TX - M_PWR_SAV); + /* + * Cancel any background scan. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_anyscan(vap); + /* + * Find the node for the destination so we can do + * things like power save and fast frames aggregation. + * + * NB: past this point various code assumes the first + * mbuf has the 802.3 header present (and contiguous). + */ + ni = NULL; + if (m->m_len < sizeof(struct ether_header) && + (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "discard frame, %s\n", "m_pullup failed"); + vap->iv_stats.is_tx_nobuf++; /* XXX */ + ifp->if_oerrors++; + continue; + } + eh = mtod(m, struct ether_header *); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + if (IS_DWDS(vap)) { + /* + * Only unicast frames from the above go out + * DWDS vaps; multicast frames are handled by + * dispatching the frame as it comes through + * the AP vap (see below). + */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, + eh->ether_dhost, "mcast", "%s", "on DWDS"); + vap->iv_stats.is_dwds_mcast++; + m_freem(m); + continue; + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* + * Spam DWDS vap's w/ multicast traffic. + */ + /* XXX only if dwds in use? */ + ieee80211_dwds_mcast(vap, m); + } + } + ni = ieee80211_find_txnode(vap, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + ifp->if_oerrors++; + m_freem(m); + continue; + } + /* XXX AUTH'd */ + if (ni->ni_associd == 0) { + /* + * Destination is not associated; must special + * case DWDS where we point iv_bss at the node + * for the associated station. + * XXX adhoc mode? + */ + if (ni != vap->iv_bss || IS_DWDS(vap)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "sta not associated (type 0x%04x)", + htons(eh->ether_type)); + vap->iv_stats.is_tx_notassoc++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + continue; + } + } + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m->m_flags & M_PWR_SAV) == 0) { + /* + * Station in power save mode; pass the frame + * to the 802.11 layer and continue. We'll get + * the frame back when the time is right. + * XXX lose WDS vap linkage? + */ + ieee80211_pwrsave(ni, m); + ieee80211_free_node(ni); + continue; + } + /* calculate priority so drivers can find the tx queue */ + if (ieee80211_classify(ni, m)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "%s", "classification failure"); + vap->iv_stats.is_tx_classify++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + continue; + } + + BPF_MTAP(ifp, m); /* 802.11 tx path */ + + /* + * XXX When ni is associated with a WDS link then + * the vap will be the WDS vap but ni_vap will point + * to the ap vap the station associated to. Once + * we handoff the packet to the driver the callback + * to ieee80211_encap won't be able to tell if the + * packet should be encapsulated for WDS or not (e.g. + * multicast frames will not be handled correctly). + * We hack this by marking the mbuf so ieee80211_encap + * can do the right thing. + */ + if (vap->iv_opmode == IEEE80211_M_WDS) + m->m_flags |= M_WDS; + else + m->m_flags &= ~M_WDS; + + /* + * Stash the node pointer and hand the frame off to + * the underlying device. Note that we do this after + * any call to ieee80211_dwds_mcast because that code + * uses any existing value for rcvif. + */ + m->m_pkthdr.rcvif = (void *)ni; + + /* XXX defer if_start calls? */ + IFQ_HANDOFF(parent, m, error); + if (error != 0) { + /* NB: IFQ_HANDOFF reclaims mbuf */ + ieee80211_free_node(ni); + } else { + ifp->if_opackets++; + } + ic->ic_lastdata = ticks; + } +#undef IS_DWDS +} + +/* + * 802.11 output routine. This is (currently) used only to + * connect bpf write calls to the 802.11 layer for injecting + * raw 802.11 frames. Note we locate the ieee80211com from + * the ifnet using a spare field setup at attach time. This + * will go away when the virtual ap support comes in. + */ +int +ieee80211_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt0) +{ +#define senderr(e) do { error = (e); goto bad;} while (0) + struct ieee80211_node *ni = NULL; + struct ieee80211vap *vap; + struct ieee80211_frame *wh; + int error; + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { + /* + * Short-circuit requests if the vap is marked OACTIVE + * as this is used when tearing down state to indicate + * the vap may be gone. This can also happen because a + * packet came down through ieee80211_start before the + * vap entered RUN state in which case it's also ok to + * just drop the frame. This should not be necessary + * but callers of if_output don't check OACTIVE. + */ + senderr(ENETDOWN); + } + vap = ifp->if_softc; + /* + * Hand to the 802.3 code if not tagged as + * a raw 802.11 frame. + */ + if (dst->sa_family != AF_IEEE80211) + return vap->iv_output(ifp, m, dst, rt0); +#ifdef MAC + error = mac_check_ifnet_transmit(ifp, m); + if (error) + senderr(error); +#endif + if (ifp->if_flags & IFF_MONITOR) + senderr(ENETDOWN); + if (!IFNET_IS_UP_RUNNING(ifp)) + senderr(ENETDOWN); + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + "block %s frame in CAC state\n", "raw data"); + vap->iv_stats.is_tx_badstate++; + senderr(EIO); /* XXX */ + } + /* XXX bypass bridge, pfil, carp, etc. */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) + senderr(EIO); /* XXX */ + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) + senderr(EIO); /* XXX */ + + /* locate destination node */ + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + case IEEE80211_FC1_DIR_FROMDS: + ni = ieee80211_find_txnode(vap, wh->i_addr1); + break; + case IEEE80211_FC1_DIR_TODS: + case IEEE80211_FC1_DIR_DSTODS: + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) + senderr(EIO); /* XXX */ + ni = ieee80211_find_txnode(vap, wh->i_addr3); + break; + default: + senderr(EIO); /* XXX */ + } + if (ni == NULL) { + /* + * Permit packets w/ bpf params through regardless + * (see below about sa_len). + */ + if (dst->sa_len == 0) + senderr(EHOSTUNREACH); + ni = ieee80211_ref_node(vap->iv_bss); + } + + /* + * Sanitize mbuf for net80211 flags leaked from above. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~M_80211_TX; + + /* calculate priority so drivers can find the tx queue */ + /* XXX assumes an 802.3 frame */ + if (ieee80211_classify(ni, m)) + senderr(EIO); /* XXX */ + + BPF_MTAP(ifp, m); + + /* + * NB: DLT_IEEE802_11_RADIO identifies the parameters are + * present by setting the sa_len field of the sockaddr (yes, + * this is a hack). + * NB: we assume sa_data is suitably aligned to cast. + */ + return vap->iv_ic->ic_raw_xmit(ni, m, + (const struct ieee80211_bpf_params *)(dst->sa_len ? + dst->sa_data : NULL)); +bad: + if (m != NULL) + m_freem(m); + if (ni != NULL) + ieee80211_free_node(ni); + return error; +#undef senderr +} + +/* * Set the direction field and address fields of an outgoing * non-QoS frame. Note this should be called early on in * constructing a frame as it sets i_fc[1]; other bits can * then be or'd in. */ static void -ieee80211_send_setup(struct ieee80211com *ic, +ieee80211_send_setup( struct ieee80211_node *ni, struct ieee80211_frame *wh, int type, @@ -101,7 +423,9 @@ ieee80211_send_setup(struct ieee80211com *ic, wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { - switch (ic->ic_opmode) { + struct ieee80211vap *vap = ni->ni_vap; + + switch (vap->iv_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; IEEE80211_ADDR_COPY(wh->i_addr1, bssid); @@ -123,9 +447,8 @@ ieee80211_send_setup(struct ieee80211com *ic, break; case IEEE80211_M_WDS: wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; - /* XXX cheat, bssid holds RA */ - IEEE80211_ADDR_COPY(wh->i_addr1, bssid); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, da); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); break; @@ -139,6 +462,7 @@ ieee80211_send_setup(struct ieee80211com *ic, IEEE80211_ADDR_COPY(wh->i_addr3, bssid); } *(uint16_t *)&wh->i_dur[0] = 0; + /* XXX probe response use per-vap seq#? */ /* NB: use non-QoS tid */ *(uint16_t *)&wh->i_seq[0] = htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT); @@ -151,55 +475,55 @@ ieee80211_send_setup(struct ieee80211com *ic, * must have a reference as the pointer will be passed to the driver * and potentially held for a long time. If the frame is successfully * dispatched to the driver, then it is responsible for freeing the - * reference (and potentially free'ing up any associated storage). + * reference (and potentially free'ing up any associated storage); + * otherwise deal with reclaiming any reference (on error). */ int -ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, - struct mbuf *m, int type) +ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type) { - struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; KASSERT(ni != NULL, ("null node")); - /* - * Yech, hack alert! We want to pass the node down to the - * driver's start routine. If we don't do so then the start - * routine must immediately look it up again and that can - * cause a lock order reversal if, for example, this frame - * is being sent because the station is being timedout and - * the frame being sent is a DEAUTH message. We could stick - * this in an m_tag and tack that on to the mbuf. However - * that's rather expensive to do for every frame so instead - * we stuff it in the rcvif field since outbound frames do - * not (presently) use this. - */ + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + ni, "block %s frame in CAC state", + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT]); + vap->iv_stats.is_tx_badstate++; + ieee80211_free_node(ni); + m_freem(m); + return EIO; /* XXX */ + } + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); - if (m == NULL) + if (m == NULL) { + ieee80211_free_node(ni); return ENOMEM; - KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); - m->m_pkthdr.rcvif = (void *)ni; + } wh = mtod(m, struct ieee80211_frame *); - ieee80211_send_setup(ic, ni, wh, + ieee80211_send_setup(ni, wh, IEEE80211_FC0_TYPE_MGT | type, - ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); + vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { m->m_flags &= ~M_LINK0; - IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, - "[%s] encrypting frame (%s)\n", - ether_sprintf(wh->i_addr1), __func__); + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, + "encrypting frame (%s)", __func__); wh->i_fc[1] |= IEEE80211_FC1_WEP; } - if (ni->ni_flags & IEEE80211_NODE_QOS) { - /* NB: force all management frames to the highest queue */ + if (type != IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + /* NB: force non-ProbeResp frames to the highest queue */ M_WME_SETAC(m, WME_AC_VO); } else M_WME_SETAC(m, WME_AC_BE); #ifdef IEEE80211_DEBUG /* avoid printing too many frames */ - if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || - ieee80211_msg_dumppkts(ic)) { + if ((ieee80211_msg_debug(vap) && doprint(vap, type)) || + ieee80211_msg_dumppkts(vap)) { printf("[%s] send %s on channel %u\n", ether_sprintf(wh->i_addr1), ieee80211_mgt_subtype_name[ @@ -209,135 +533,8 @@ ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, } #endif IEEE80211_NODE_STAT(ni, tx_mgmt); - IF_ENQUEUE(&ic->ic_mgtq, m); - if_start(ifp); - ifp->if_opackets++; - - return 0; -} - -/* - * Raw packet transmit stub for legacy drivers. - * Send the packet through the mgt q so we bypass - * the normal encapsulation work. - */ -int -ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) -{ - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - - m->m_pkthdr.rcvif = (void *) ni; - IF_ENQUEUE(&ic->ic_mgtq, m); - if_start(ifp); - ifp->if_opackets++; - - return 0; -} - -/* - * 802.11 output routine. This is (currently) used only to - * connect bpf write calls to the 802.11 layer for injecting - * raw 802.11 frames. Note we locate the ieee80211com from - * the ifnet using a spare field setup at attach time. This - * will go away when the virtual ap support comes in. - */ -int -ieee80211_output(struct ifnet *ifp, struct mbuf *m, - struct sockaddr *dst, struct rtentry *rt0) -{ -#define senderr(e) do { error = (e); goto bad;} while (0) - struct ieee80211com *ic = ifp->if_llsoftc; /* XXX */ - struct ieee80211_node *ni = NULL; - struct ieee80211_frame *wh; - int error; - - /* - * Hand to the 802.3 code if not tagged as - * a raw 802.11 frame. - */ - if (dst->sa_family != AF_IEEE80211) - return ether_output(ifp, m, dst, rt0); -#ifdef MAC - error = mac_check_ifnet_transmit(ifp, m); - if (error) - senderr(error); -#endif - if (ifp->if_flags & IFF_MONITOR) - senderr(ENETDOWN); - if ((ifp->if_flags & IFF_UP) == 0) - senderr(ENETDOWN); - /* XXX bypass bridge, pfil, carp, etc. */ - - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) - senderr(EIO); /* XXX */ - wh = mtod(m, struct ieee80211_frame *); - if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != - IEEE80211_FC0_VERSION_0) - senderr(EIO); /* XXX */ - - /* locate destination node */ - switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { - case IEEE80211_FC1_DIR_NODS: - case IEEE80211_FC1_DIR_FROMDS: - ni = ieee80211_find_txnode(ic, wh->i_addr1); - break; - case IEEE80211_FC1_DIR_TODS: - case IEEE80211_FC1_DIR_DSTODS: - if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) - senderr(EIO); /* XXX */ - ni = ieee80211_find_txnode(ic, wh->i_addr3); - break; - default: - senderr(EIO); /* XXX */ - } - if (ni == NULL) { - /* - * Permit packets w/ bpf params through regardless - * (see below about sa_len). - */ - if (dst->sa_len == 0) - senderr(EHOSTUNREACH); - ni = ieee80211_ref_node(ic->ic_bss); - } - - /* XXX ctrl frames should go through */ - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && - (m->m_flags & M_PWR_SAV) == 0) { - /* - * Station in power save mode; pass the frame - * to the 802.11 layer and continue. We'll get - * the frame back when the time is right. - */ - ieee80211_pwrsave(ni, m); - error = 0; - goto reclaim; - } - - /* calculate priority so drivers can find the tx queue */ - /* XXX assumes an 802.3 frame */ - if (ieee80211_classify(ic, m, ni)) - senderr(EIO); /* XXX */ - - BPF_MTAP(ifp, m); - /* - * NB: DLT_IEEE802_11_RADIO identifies the parameters are - * present by setting the sa_len field of the sockaddr (yes, - * this is a hack). - * NB: we assume sa_data is suitably aligned to cast. - */ - return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *) - (dst->sa_len ? dst->sa_data : NULL)); -bad: - if (m != NULL) - m_freem(m); -reclaim: - if (ni != NULL) - ieee80211_free_node(ni); - return error; -#undef senderr + return ic->ic_raw_xmit(ni, m, NULL); } /* @@ -345,50 +542,61 @@ reclaim: * * NB: the caller is assumed to have setup a node reference * for use; this is necessary to deal with a race condition - * when probing for inactive stations. + * when probing for inactive stations. Like ieee80211_mgmt_output + * we must cleanup any node reference on error; however we + * can safely just unref it as we know it will never be the + * last reference to the node. */ int ieee80211_send_nulldata(struct ieee80211_node *ni) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; struct mbuf *m; struct ieee80211_frame *wh; - MGETHDR(m, M_NOWAIT, MT_DATA); + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + ni, "block %s frame in CAC state", "null data"); + ieee80211_unref_node(&ni); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + m = m_gethdr(M_NOWAIT, MT_HEADER); if (m == NULL) { /* XXX debug msg */ ieee80211_unref_node(&ni); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return ENOMEM; } MH_ALIGN(m, sizeof(struct ieee80211_frame)); - m->m_pkthdr.rcvif = (void *) ni; wh = mtod(m, struct ieee80211_frame *); - ieee80211_send_setup(ic, ni, wh, + ieee80211_send_setup(ni, wh, IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, - ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); - /* NB: power management bit is never sent by an AP */ - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && - ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_opmode != IEEE80211_M_WDS) - wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; - m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); + vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); + if (vap->iv_opmode != IEEE80211_M_WDS) { + /* NB: power management bit is never sent by an AP */ + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + vap->iv_opmode != IEEE80211_M_HOSTAP) + wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; + m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); + } else { + /* NB: 4-address frame */ + m->m_len = m->m_pkthdr.len = + sizeof(struct ieee80211_frame_addr4); + } M_WME_SETAC(m, WME_AC_BE); IEEE80211_NODE_STAT(ni, tx_data); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, - "[%s] send null data frame on channel %u, pwr mgt %s\n", - ether_sprintf(ni->ni_macaddr), + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni, + "send null data frame on channel %u, pwr mgt %s", ieee80211_chan2ieee(ic, ic->ic_curchan), wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); - IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ - if_start(ifp); - - return 0; + return ic->ic_raw_xmit(ni, m, NULL); } /* @@ -398,13 +606,23 @@ ieee80211_send_nulldata(struct ieee80211_node *ni) * applied. */ int -ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) +ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m) { + const struct ether_header *eh = mtod(m, struct ether_header *); int v_wme_ac, d_wme_ac, ac; -#ifdef INET - struct ether_header *eh; -#endif + /* + * Always promote PAE/EAPOL frames to high priority. + */ + if (eh->ether_type == htons(ETHERTYPE_PAE)) { + /* NB: mark so others don't need to check header */ + m->m_flags |= M_EAPOL; + ac = WME_AC_VO; + goto done; + } + /* + * Non-qos traffic goes to BE. + */ if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { ac = WME_AC_BE; goto done; @@ -430,7 +648,6 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod } #ifdef INET - eh = mtod(m, struct ether_header *); if (eh->ether_type == htons(ETHERTYPE_IP)) { uint8_t tos; /* @@ -459,13 +676,15 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod /* * Apply ACM policy. */ - if (ic->ic_opmode == IEEE80211_M_STA) { + if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) { static const int acmap[4] = { WME_AC_BK, /* WME_AC_BE */ WME_AC_BK, /* WME_AC_BK */ WME_AC_BE, /* WME_AC_VI */ WME_AC_VI, /* WME_AC_VO */ }; + struct ieee80211com *ic = ni->ni_ic; + while (ac != WME_AC_BK && ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) ac = acmap[ac]; @@ -482,11 +701,11 @@ done: * and fail rudely if they don't find the space they need. */ static struct mbuf * -ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, +ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize, struct ieee80211_key *key, struct mbuf *m) { #define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) - int needed_space = ic->ic_headroom + hdrsize; + int needed_space = vap->iv_ic->ic_headroom + hdrsize; if (key != NULL) { /* XXX belongs in crypto code? */ @@ -501,9 +720,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) { m = m_unshare(m, M_NOWAIT); if (m == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: cannot get writable mbuf\n", __func__); - ic->ic_stats.is_tx_nobuf++; /* XXX new stat */ + vap->iv_stats.is_tx_nobuf++; /* XXX new stat */ return NULL; } } @@ -520,9 +739,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); if (n == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: cannot expand storage\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; m_freem(m); return NULL; } @@ -564,13 +783,14 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, * we fall back to the default transmit key. */ static __inline struct ieee80211_key * -ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_crypto_getucastkey(struct ieee80211vap *vap, + struct ieee80211_node *ni) { if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { - if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || - IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey])) + if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || + IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) return NULL; - return &ic->ic_nw_keys[ic->ic_def_txkey]; + return &vap->iv_nw_keys[vap->iv_def_txkey]; } else { return &ni->ni_ucastkey; } @@ -582,12 +802,13 @@ ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni) * the default tx key. */ static __inline struct ieee80211_key * -ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_crypto_getmcastkey(struct ieee80211vap *vap, + struct ieee80211_node *ni) { - if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || - IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey])) + if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || + IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) return NULL; - return &ic->ic_nw_keys[ic->ic_def_txkey]; + return &vap->iv_nw_keys[vap->iv_def_txkey]; } /* @@ -595,16 +816,21 @@ ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni) * If an error is encountered NULL is returned. The caller is required * to provide a node reference and pullup the ethernet header in the * first mbuf. + * + * NB: Packet is assumed to be processed by ieee80211_classify which + * marked EAPOL frames w/ M_EAPOL. */ struct mbuf * -ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni) +ieee80211_encap(struct ieee80211_node *ni, struct mbuf *m) { +#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh)) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; struct ether_header eh; struct ieee80211_frame *wh; struct ieee80211_key *key; struct llc *llc; - int hdrsize, datalen, addqos, txfrag, isff; + int hdrsize, hdrspace, datalen, addqos, txfrag, isff, is4addr; /* * Copy existing Ethernet header to a safe place. The @@ -612,7 +838,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * reorganizing state for the final encapsulation. */ KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); - memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); + ETHER_HEADER_COPY(&eh, mtod(m, caddr_t)); /* * Insure space for additional headers. First identify @@ -626,23 +852,24 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * buffer may not be expanded as needed by the cipher * routines, but they will/should discard it. */ - if (ic->ic_flags & IEEE80211_F_PRIVACY) { - if (ic->ic_opmode == IEEE80211_M_STA || - !IEEE80211_IS_MULTICAST(eh.ether_dhost)) - key = ieee80211_crypto_getucastkey(ic, ni); + if (vap->iv_flags & IEEE80211_F_PRIVACY) { + if (vap->iv_opmode == IEEE80211_M_STA || + !IEEE80211_IS_MULTICAST(eh.ether_dhost) || + (vap->iv_opmode == IEEE80211_M_WDS && + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) + key = ieee80211_crypto_getucastkey(vap, ni); else - key = ieee80211_crypto_getmcastkey(ic, ni); - if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, - "[%s] no default transmit key (%s) deftxkey %u\n", - ether_sprintf(eh.ether_dhost), __func__, - ic->ic_def_txkey); - ic->ic_stats.is_tx_nodefkey++; + key = ieee80211_crypto_getmcastkey(vap, ni); + if (key == NULL && (m->m_flags & M_EAPOL) == 0) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, + eh.ether_dhost, + "no default transmit key (%s) deftxkey %u", + __func__, vap->iv_def_txkey); + vap->iv_stats.is_tx_nodefkey++; goto bad; } } else key = NULL; - /* XXX 4-address format */ /* * XXX Some ap's don't handle QoS-encapsulated EAPOL * frames so suppress use. This may be an issue if other @@ -651,13 +878,31 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * configurable. */ addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) && - eh.ether_type != htons(ETHERTYPE_PAE); + (m->m_flags & M_EAPOL) == 0; if (addqos) hdrsize = sizeof(struct ieee80211_qosframe); else hdrsize = sizeof(struct ieee80211_frame); + /* + * 4-address frames need to be generated for: + * o packets sent through a WDS vap (M_WDS || IEEE80211_M_WDS) + * o packets relayed by a station operating with dynamic WDS + * (IEEE80211_M_STA+IEEE80211_F_DWDS and src address) + */ + is4addr = (m->m_flags & M_WDS) || + vap->iv_opmode == IEEE80211_M_WDS || /* XXX redundant? */ + (vap->iv_opmode == IEEE80211_M_STA && + (vap->iv_flags & IEEE80211_F_DWDS) && + !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)); + if (is4addr) + hdrsize += IEEE80211_ADDR_LEN; + /* + * Honor driver DATAPAD requirement. + */ if (ic->ic_flags & IEEE80211_F_DATAPAD) - hdrsize = roundup(hdrsize, sizeof(uint32_t)); + hdrspace = roundup(hdrsize, sizeof(uint32_t)); + else + hdrspace = hdrsize; if ((isff = m->m_flags & M_FF) != 0) { struct mbuf *m2; @@ -671,7 +916,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, */ m2 = m->m_nextpkt; if (m2 == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: only one frame\n", __func__); goto bad; } @@ -681,8 +926,8 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * layout; this allocates space according to what * ieee80211_encap_fastframe will do. */ - m = ieee80211_mbuf_adjust(ic, - hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 + + m = ieee80211_mbuf_adjust(vap, + hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 + sizeof(struct ether_header), key, m); if (m == NULL) { @@ -697,22 +942,22 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, * at the end of first frame. */ KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); - memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header)); - m2 = ieee80211_mbuf_adjust(ic, + ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t)); + m2 = ieee80211_mbuf_adjust(vap, ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header), NULL, m2); if (m2 == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; } - m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2); + m = ieee80211_encap_fastframe(vap, m, &eh, m2, &eh2); if (m == NULL) goto bad; } else { /* * Normal frame. */ - m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); + m = ieee80211_mbuf_adjust(vap, hdrspace, key, m); if (m == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; @@ -729,15 +974,21 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, } datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ - M_PREPEND(m, hdrsize, M_DONTWAIT); + M_PREPEND(m, hdrspace, M_DONTWAIT); if (m == NULL) { - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; goto bad; } wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; *(uint16_t *)wh->i_dur = 0; - switch (ic->ic_opmode) { + if (is4addr) { + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); + } else switch (vap->iv_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); @@ -750,10 +1001,10 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); /* - * NB: always use the bssid from ic_bss as the + * NB: always use the bssid from iv_bss as the * neighbor's may be stale after an ibss merge */ - IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid); break; case IEEE80211_M_HOSTAP: wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; @@ -762,42 +1013,47 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); break; case IEEE80211_M_MONITOR: - case IEEE80211_M_WDS: + case IEEE80211_M_WDS: /* NB: is4addr should always be true */ goto bad; } if (m->m_flags & M_MORE_DATA) wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; if (addqos) { - struct ieee80211_qosframe *qwh = - (struct ieee80211_qosframe *) wh; + uint8_t *qos; int ac, tid; + if (is4addr) { + qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; + } else + qos = ((struct ieee80211_qosframe *) wh)->i_qos; ac = M_WME_GETAC(m); /* map from access class/queue to 11e header priorty value */ tid = WME_AC_TO_TID(ac); - qwh->i_qos[0] = tid & IEEE80211_QOS_TID; + qos[0] = tid & IEEE80211_QOS_TID; /* * Check if A-MPDU tx aggregation is setup or if we * should try to enable it. The sta must be associated - * with HT and A-MPDU enabled for use. On the first - * frame that goes out We issue an ADDBA request and - * wait for a reply. The frame being encapsulated - * will go out w/o using A-MPDU, or possibly it might - * be collected by the driver and held/retransmit. - * ieee80211_ampdu_request handles staggering requests - * in case the receiver NAK's us or we are otherwise - * unable to establish a BA stream. + * with HT and A-MPDU enabled for use. When the policy + * routine decides we should enable A-MPDU we issue an + * ADDBA request and wait for a reply. The frame being + * encapsulated will go out w/o using A-MPDU, or possibly + * it might be collected by the driver and held/retransmit. + * The default ic_ampdu_enable routine handles staggering + * ADDBA requests in case the receiver NAK's us or we are + * otherwise unable to establish a BA stream. */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && - (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) { + (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)) { struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; + ieee80211_txampdu_count_packet(tap); if (IEEE80211_AMPDU_RUNNING(tap)) { /* * Operational, mark frame for aggregation. */ - qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA; - } else if (!IEEE80211_AMPDU_REQUESTED(tap)) { + qos[0] |= IEEE80211_QOS_ACKPOLICY_BA; + } else if (!IEEE80211_AMPDU_REQUESTED(tap) && + ic->ic_ampdu_enable(ni, tap)) { /* * Not negotiated yet, request service. */ @@ -806,9 +1062,9 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, } /* XXX works even when BA marked above */ if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) - qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; - qwh->i_qos[1] = 0; - qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; + qos[1] = 0; + wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; *(uint16_t *)wh->i_seq = htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); @@ -819,38 +1075,32 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, ni->ni_txseqs[IEEE80211_NONQOS_TID]++; } /* check if xmit fragmentation is required */ - txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold && + txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold && !IEEE80211_IS_MULTICAST(wh->i_addr1) && - (ic->ic_caps & IEEE80211_C_TXFRAG) && + (vap->iv_caps & IEEE80211_C_TXFRAG) && !isff); /* NB: don't fragment ff's */ if (key != NULL) { /* * IEEE 802.1X: send EAPOL frames always in the clear. * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. */ - if (eh.ether_type != htons(ETHERTYPE_PAE) || - ((ic->ic_flags & IEEE80211_F_WPA) && - (ic->ic_opmode == IEEE80211_M_STA ? + if ((m->m_flags & M_EAPOL) == 0 || + ((vap->iv_flags & IEEE80211_F_WPA) && + (vap->iv_opmode == IEEE80211_M_STA ? !IEEE80211_KEY_UNDEFINED(key) : !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { wh->i_fc[1] |= IEEE80211_FC1_WEP; - if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, - "[%s] enmic failed, discard frame\n", - ether_sprintf(eh.ether_dhost)); - ic->ic_stats.is_crypto_enmicfail++; + if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, + eh.ether_dhost, + "%s", "enmic failed, discard frame"); + vap->iv_stats.is_crypto_enmicfail++; goto bad; } } } - /* - * NB: frag flags may leak from above; they should only - * be set on return to the caller if we fragment at - * the 802.11 layer. - */ - m->m_flags &= ~(M_FRAG | M_FIRSTFRAG); - if (txfrag && !ieee80211_fragment(ic, m, hdrsize, - key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold)) + if (txfrag && !ieee80211_fragment(vap, m, hdrsize, + key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold)) goto bad; IEEE80211_NODE_STAT(ni, tx_data); @@ -860,11 +1110,16 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, IEEE80211_NODE_STAT(ni, tx_ucast); IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); + /* XXX fragmented frames not handled */ + if (bpf_peers_present(vap->iv_rawbpf)) + bpf_mtap(vap->iv_rawbpf, m); + return m; bad: if (m != NULL) m_freem(m); return NULL; +#undef WH4 } /* @@ -875,7 +1130,7 @@ bad: * type that specifies the payload size). */ static struct mbuf * -ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, +ieee80211_encap1(struct ieee80211vap *vap, struct mbuf *m, const struct ether_header *eh) { struct llc *llc; @@ -894,9 +1149,9 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT); if (m == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no space for ether_header\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return NULL; } ETHER_HEADER_COPY(mtod(m, void *), eh); @@ -914,7 +1169,7 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, * problem (should not happen). */ static struct mbuf * -ieee80211_encap_fastframe(struct ieee80211com *ic, +ieee80211_encap_fastframe(struct ieee80211vap *vap, struct mbuf *m1, const struct ether_header *eh1, struct mbuf *m2, const struct ether_header *eh2) { @@ -925,12 +1180,12 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, /* * First, each frame gets a standard encapsulation. */ - m1 = ieee80211_encap1(ic, m1, eh1); + m1 = ieee80211_encap1(vap, m1, eh1); if (m1 == NULL) { m_freem(m2); return NULL; } - m2 = ieee80211_encap1(ic, m2, eh2); + m2 = ieee80211_encap1(vap, m2, eh2); if (m2 == NULL) { m_freem(m1); return NULL; @@ -969,18 +1224,18 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, m1->m_pkthdr.len += m2->m_pkthdr.len; M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT); if (m1 == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no space for tunnel header\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return NULL; } memset(mtod(m1, void *), 0, sizeof(uint32_t)+2); M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT); if (m1 == NULL) { /* XXX cannot happen */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no space for llc header\n", __func__); - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; return NULL; } llc = mtod(m1, struct llc *); @@ -991,7 +1246,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2; llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE); - ic->ic_stats.is_ff_encap++; + vap->iv_stats.is_ff_encap++; return m1; } @@ -1005,7 +1260,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic, * packet's mbufs but that is significantly more complicated. */ static int -ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, +ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, u_int hdrsize, u_int ciphdrsize, u_int mtu) { struct ieee80211_frame *wh, *whf; @@ -1028,6 +1283,7 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, fragsize = totalhdrsize + remainder; if (fragsize > mtu) fragsize = mtu; + /* XXX fragsize can be >2048! */ KASSERT(fragsize < MCLBYTES, ("fragment size %u too big!", fragsize)); if (fragsize > MHLEN) @@ -1073,8 +1329,8 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); m0->m_flags |= M_FIRSTFRAG | M_FRAG; - ic->ic_stats.is_tx_fragframes++; - ic->ic_stats.is_tx_frags += fragno-1; + vap->iv_stats.is_tx_fragframes++; + vap->iv_stats.is_tx_frags += fragno-1; return 1; bad: @@ -1125,7 +1381,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) } /* - * Add an ssid elemet to a frame. + * Add an ssid element to a frame. */ static uint8_t * ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) @@ -1157,188 +1413,39 @@ ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) return frm; } +/* + * Add a CFParams element to a frame. + */ static uint8_t * -ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie) +ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic) { -#define WPA_OUI_BYTES 0x00, 0x50, 0xf2 #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) -#define ADDSELECTOR(frm, sel) do { \ - memcpy(frm, sel, 4); \ - frm += 4; \ -} while (0) - static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; - static const uint8_t cipher_suite[][4] = { - { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */ - { WPA_OUI_BYTES, WPA_CSE_TKIP }, - { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */ - { WPA_OUI_BYTES, WPA_CSE_CCMP }, - { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ - { WPA_OUI_BYTES, WPA_CSE_NULL }, - }; - static const uint8_t wep104_suite[4] = - { WPA_OUI_BYTES, WPA_CSE_WEP104 }; - static const uint8_t key_mgt_unspec[4] = - { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC }; - static const uint8_t key_mgt_psk[4] = - { WPA_OUI_BYTES, WPA_ASE_8021X_PSK }; - const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; - uint8_t *frm = ie; - uint8_t *selcnt; - - *frm++ = IEEE80211_ELEMID_VENDOR; - *frm++ = 0; /* length filled in below */ - memcpy(frm, oui, sizeof(oui)); /* WPA OUI */ - frm += sizeof(oui); - ADDSHORT(frm, WPA_VERSION); - - /* XXX filter out CKIP */ - - /* multicast cipher */ - if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && - rsn->rsn_mcastkeylen >= 13) - ADDSELECTOR(frm, wep104_suite); - else - ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); - - /* unicast cipher list */ - selcnt = frm; - ADDSHORT(frm, 0); /* selector count */ - if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); - } - if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); - } - - /* authenticator selector list */ - selcnt = frm; - ADDSHORT(frm, 0); /* selector count */ - if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { - selcnt[0]++; - ADDSELECTOR(frm, key_mgt_unspec); - } - if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { - selcnt[0]++; - ADDSELECTOR(frm, key_mgt_psk); - } - - /* optional capabilities */ - if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH) - ADDSHORT(frm, rsn->rsn_caps); - - /* calculate element length */ - ie[1] = frm - ie - 2; - KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), - ("WPA IE too big, %u > %zu", - ie[1]+2, sizeof(struct ieee80211_ie_wpa))); + *frm++ = IEEE80211_ELEMID_CFPARMS; + *frm++ = 6; + *frm++ = 0; /* CFP count */ + *frm++ = 2; /* CFP period */ + ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */ + ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */ return frm; #undef ADDSHORT -#undef ADDSELECTOR -#undef WPA_OUI_BYTES } -static uint8_t * -ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie) +static __inline uint8_t * +add_appie(uint8_t *frm, const struct ieee80211_appie *ie) { -#define RSN_OUI_BYTES 0x00, 0x0f, 0xac -#define ADDSHORT(frm, v) do { \ - frm[0] = (v) & 0xff; \ - frm[1] = (v) >> 8; \ - frm += 2; \ -} while (0) -#define ADDSELECTOR(frm, sel) do { \ - memcpy(frm, sel, 4); \ - frm += 4; \ -} while (0) - static const uint8_t cipher_suite[][4] = { - { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */ - { RSN_OUI_BYTES, RSN_CSE_TKIP }, - { RSN_OUI_BYTES, RSN_CSE_WRAP }, - { RSN_OUI_BYTES, RSN_CSE_CCMP }, - { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ - { RSN_OUI_BYTES, RSN_CSE_NULL }, - }; - static const uint8_t wep104_suite[4] = - { RSN_OUI_BYTES, RSN_CSE_WEP104 }; - static const uint8_t key_mgt_unspec[4] = - { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC }; - static const uint8_t key_mgt_psk[4] = - { RSN_OUI_BYTES, RSN_ASE_8021X_PSK }; - const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; - uint8_t *frm = ie; - uint8_t *selcnt; - - *frm++ = IEEE80211_ELEMID_RSN; - *frm++ = 0; /* length filled in below */ - ADDSHORT(frm, RSN_VERSION); - - /* XXX filter out CKIP */ - - /* multicast cipher */ - if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && - rsn->rsn_mcastkeylen >= 13) - ADDSELECTOR(frm, wep104_suite); - else - ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); - - /* unicast cipher list */ - selcnt = frm; - ADDSHORT(frm, 0); /* selector count */ - if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); - } - if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { - selcnt[0]++; - ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); - } - - /* authenticator selector list */ - selcnt = frm; - ADDSHORT(frm, 0); /* selector count */ - if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { - selcnt[0]++; - ADDSELECTOR(frm, key_mgt_unspec); - } - if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { - selcnt[0]++; - ADDSELECTOR(frm, key_mgt_psk); - } - - /* optional capabilities */ - ADDSHORT(frm, rsn->rsn_caps); - /* XXX PMKID */ - - /* calculate element length */ - ie[1] = frm - ie - 2; - KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), - ("RSN IE too big, %u > %zu", - ie[1]+2, sizeof(struct ieee80211_ie_wpa))); - return frm; -#undef ADDSELECTOR -#undef ADDSHORT -#undef RSN_OUI_BYTES + memcpy(frm, ie->ie_data, ie->ie_len); + return frm + ie->ie_len; } -/* - * Add a WPA/RSN element to a frame. - */ -static uint8_t * -ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic) +static __inline uint8_t * +add_ie(uint8_t *frm, const uint8_t *ie) { - - KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!")); - if (ic->ic_flags & IEEE80211_F_WPA2) - frm = ieee80211_setup_rsn_ie(ic, frm); - if (ic->ic_flags & IEEE80211_F_WPA1) - frm = ieee80211_setup_wpa_ie(ic, frm); - return frm; + memcpy(frm, ie, 2 + ie[1]); + return frm + 2 + ie[1]; } #define WME_OUI_BYTES 0x00, 0x50, 0xf2 @@ -1432,6 +1539,94 @@ ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix) #undef ATH_OUI_BYTES /* + * Add an 11h Power Constraint element to a frame. + */ +static uint8_t * +ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap) +{ + const struct ieee80211_channel *c = vap->iv_bss->ni_chan; + /* XXX per-vap tx power limit? */ + int8_t limit = vap->iv_ic->ic_txpowlimit / 2; + + frm[0] = IEEE80211_ELEMID_PWRCNSTR; + frm[1] = 1; + frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0; + return frm + 3; +} + +/* + * Add an 11h Power Capability element to a frame. + */ +static uint8_t * +ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c) +{ + frm[0] = IEEE80211_ELEMID_PWRCAP; + frm[1] = 2; + frm[2] = c->ic_minpower; + frm[3] = c->ic_maxpower; + return frm + 4; +} + +/* + * Add an 11h Supported Channels element to a frame. + */ +static uint8_t * +ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic) +{ + static const int ielen = 26; + + frm[0] = IEEE80211_ELEMID_SUPPCHAN; + frm[1] = ielen; + /* XXX not correct */ + memcpy(frm+2, ic->ic_chan_avail, ielen); + return frm + 2 + ielen; +} + +/* + * Add an 11h Channel Switch Announcement element to a frame. + * Note that we use the per-vap CSA count to adjust the global + * counter so we can use this routine to form probe response + * frames and get the current count. + */ +static uint8_t * +ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm; + + csa->csa_ie = IEEE80211_ELEMID_CHANSWITCHANN; + csa->csa_len = 3; + csa->csa_mode = 1; /* XXX force quiet on channel */ + csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan); + csa->csa_count = ic->ic_csa_count - vap->iv_csa_count; + return frm + sizeof(*csa); +} + +/* + * Add an 11h country information element to a frame. + */ +static uint8_t * +ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) +{ + + if (ic->ic_countryie == NULL || + ic->ic_countryie_chan != ic->ic_bsschan) { + /* + * Handle lazy construction of ie. This is done on + * first use and after a channel change that requires + * re-calculation. + */ + if (ic->ic_countryie != NULL) + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = ieee80211_alloc_countryie(ic); + if (ic->ic_countryie == NULL) + return frm; + ic->ic_countryie_chan = ic->ic_bsschan; + } + return add_appie(frm, ic->ic_countryie); +} + +/* * Send a probe request frame with the specified ssid * and any optional information element data. */ @@ -1440,21 +1635,28 @@ ieee80211_send_probereq(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t *ssid, size_t ssidlen, - const void *optie, size_t optielen) + const uint8_t *ssid, size_t ssidlen) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; const struct ieee80211_rateset *rs; struct mbuf *m; uint8_t *frm; + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, + "block %s frame in CAC state", "probe request"); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), @@ -1465,18 +1667,23 @@ ieee80211_send_probereq(struct ieee80211_node *ni, * prreq frame format * [tlv] ssid * [tlv] supported rates + * [tlv] RSN (optional) * [tlv] extended supported rates + * [tlv] WPA (optional) * [tlv] user-specified ie's */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), - 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + + sizeof(struct ieee80211_ie_wpa) + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) - + (optie != NULL ? optielen : 0) + + sizeof(struct ieee80211_ie_wpa) + + (vap->iv_appie_probereq != NULL ? + vap->iv_appie_probereq->ie_len : 0) ); if (m == NULL) { - ic->ic_stats.is_tx_nobuf++; + vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); return ENOMEM; } @@ -1484,62 +1691,70 @@ ieee80211_send_probereq(struct ieee80211_node *ni, frm = ieee80211_add_ssid(frm, ssid, ssidlen); rs = ieee80211_get_suprates(ic, ic->ic_curchan); frm = ieee80211_add_rates(frm, rs); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } frm = ieee80211_add_xrates(frm, rs); - - if (optie != NULL) { - memcpy(frm, optie, optielen); - frm += optielen; + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain? */ } + if (vap->iv_appie_probereq != NULL) + frm = add_appie(frm, vap->iv_appie_probereq); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); if (m == NULL) return ENOMEM; - KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); - m->m_pkthdr.rcvif = (void *)ni; wh = mtod(m, struct ieee80211_frame *); - ieee80211_send_setup(ic, ni, wh, + ieee80211_send_setup(ni, wh, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, sa, da, bssid); /* XXX power management? */ + M_WME_SETAC(m, WME_AC_BE); + IEEE80211_NODE_STAT(ni, tx_probereq); IEEE80211_NODE_STAT(ni, tx_mgmt); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, - "[%s] send probe req on channel %u\n", - ether_sprintf(wh->i_addr1), - ieee80211_chan2ieee(ic, ic->ic_curchan)); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "send probe req on channel %u bssid %s ssid \"%.*s\"\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), + ssidlen, ssid); - IF_ENQUEUE(&ic->ic_mgtq, m); - if_start(ic->ic_ifp); - return 0; + return ic->ic_raw_xmit(ni, m, NULL); } /* * Calculate capability information for mgt frames. */ static uint16_t -getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) +getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan) { + struct ieee80211com *ic = vap->iv_ic; uint16_t capinfo; - KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode")); + KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode")); - if (ic->ic_opmode == IEEE80211_M_HOSTAP) + if (vap->iv_opmode == IEEE80211_M_HOSTAP) capinfo = IEEE80211_CAPINFO_ESS; - else if (ic->ic_opmode == IEEE80211_M_IBSS) + else if (vap->iv_opmode == IEEE80211_M_IBSS) capinfo = IEEE80211_CAPINFO_IBSS; else capinfo = 0; - if (ic->ic_flags & IEEE80211_F_PRIVACY) + if (vap->iv_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(chan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; if (ic->ic_flags & IEEE80211_F_SHSLOT) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH)) + capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; return capinfo; } @@ -1549,12 +1764,13 @@ getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) * count bumped to reflect our use for an indeterminant time. */ int -ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, - int type, int arg) +ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) { #define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT) -#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) - const struct ieee80211_rateset *rs; +#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node *bss = vap->iv_bss; struct mbuf *m; uint8_t *frm; uint16_t capinfo; @@ -1567,7 +1783,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * the xmit is complete all the way in the driver. On error we * will remove our reference. */ - IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), @@ -1575,112 +1791,6 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, ieee80211_ref_node(ni); switch (type) { - case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - /* - * probe response frame format - * [8] time stamp - * [2] beacon interval - * [2] cabability information - * [tlv] ssid - * [tlv] supported rates - * [tlv] parameter set (FH/DS) - * [tlv] parameter set (IBSS) - * [tlv] extended rate phy (ERP) - * [tlv] extended supported rates - * [tlv] WPA - * [tlv] WME (optional) - * [tlv] HT capabilities - * [tlv] HT information - * [tlv] Vendor OUI HT capabilities (optional) - * [tlv] Vendor OUI HT information (optional) - * [tlv] Atheros capabilities - */ - m = ieee80211_getmgtframe(&frm, - ic->ic_headroom + sizeof(struct ieee80211_frame), - 8 - + sizeof(uint16_t) - + sizeof(uint16_t) - + 2 + IEEE80211_NWID_LEN - + 2 + IEEE80211_RATE_SIZE - + 7 /* max(7,3) */ - + 6 - + 3 - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) - /* XXX !WPA1+WPA2 fits w/o a cluster */ - + (ic->ic_flags & IEEE80211_F_WPA ? - 2*sizeof(struct ieee80211_ie_wpa) : 0) - + sizeof(struct ieee80211_wme_param) - /* XXX check for cluster requirement */ - + 2*sizeof(struct ieee80211_ie_htcap) + 4 - + 2*sizeof(struct ieee80211_ie_htinfo) + 4 - + sizeof(struct ieee80211_ath_ie) - ); - if (m == NULL) - senderr(ENOMEM, is_tx_nobuf); - - memset(frm, 0, 8); /* timestamp should be filled later */ - frm += 8; - *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval); - frm += 2; - capinfo = getcapinfo(ic, ic->ic_curchan); - *(uint16_t *)frm = htole16(capinfo); - frm += 2; - - frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, - ic->ic_bss->ni_esslen); - rs = ieee80211_get_suprates(ic, ic->ic_curchan); - frm = ieee80211_add_rates(frm, rs); - - if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) { - *frm++ = IEEE80211_ELEMID_FHPARMS; - *frm++ = 5; - *frm++ = ni->ni_fhdwell & 0x00ff; - *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff; - *frm++ = IEEE80211_FH_CHANSET( - ieee80211_chan2ieee(ic, ic->ic_curchan)); - *frm++ = IEEE80211_FH_CHANPAT( - ieee80211_chan2ieee(ic, ic->ic_curchan)); - *frm++ = ni->ni_fhindex; - } else { - *frm++ = IEEE80211_ELEMID_DSPARMS; - *frm++ = 1; - *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan); - } - - if (ic->ic_opmode == IEEE80211_M_IBSS) { - *frm++ = IEEE80211_ELEMID_IBSSPARMS; - *frm++ = 2; - *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ - } - if (ic->ic_flags & IEEE80211_F_WPA) - frm = ieee80211_add_wpa(frm, ic); - if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) - frm = ieee80211_add_erp(frm, ic); - frm = ieee80211_add_xrates(frm, rs); - /* - * NB: legacy 11b clients do not get certain ie's. - * The caller identifies such clients by passing - * a token in arg to us. Could expand this to be - * any legacy client for stuff like HT ie's. - */ - if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && - arg != IEEE80211_SEND_LEGACY_11B) { - frm = ieee80211_add_htcap(frm, ni); - frm = ieee80211_add_htinfo(frm, ni); - } - if (ic->ic_flags & IEEE80211_F_WME) - frm = ieee80211_add_wme_param(frm, &ic->ic_wme); - if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && - (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) && - arg != IEEE80211_SEND_LEGACY_11B) { - frm = ieee80211_add_htcap_vendor(frm, ni); - frm = ieee80211_add_htinfo_vendor(frm, ni); - } - if (ni->ni_ath_ie != NULL) - frm = ieee80211_add_ath(frm, ni->ni_ath_flags, - ni->ni_ath_defkeyix); - m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); - break; case IEEE80211_FC0_SUBTYPE_AUTH: status = arg >> 16; @@ -1699,10 +1809,10 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, is_shared_key = has_challenge || arg >= IEEE80211_AUTH_SHARED_RESPONSE || (arg == IEEE80211_AUTH_SHARED_REQUEST && - ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); + bss->ni_authmode == IEEE80211_AUTH_SHARED); m = ieee80211_getmgtframe(&frm, - ic->ic_headroom + sizeof(struct ieee80211_frame), + ic->ic_headroom + sizeof(struct ieee80211_frame), 3 * sizeof(uint16_t) + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) @@ -1725,9 +1835,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, m->m_pkthdr.len = m->m_len = 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, - "[%s] request encrypt frame (%s)\n", - ether_sprintf(ni->ni_macaddr), __func__); + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "request encrypt frame (%s)", __func__); m->m_flags |= M_LINK0; /* WEP-encrypt, please */ } } else @@ -1739,15 +1848,14 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, else IEEE80211_NODE_STAT(ni, tx_auth_fail); - if (ic->ic_opmode == IEEE80211_M_STA) + if (vap->iv_opmode == IEEE80211_M_STA) ieee80211_add_callback(m, ieee80211_tx_mgt_cb, - (void *) ic->ic_state); + (void *) vap->iv_state); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, - "[%s] send station deauthenticate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), arg); + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "send station deauthenticate (reason %d)", arg); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); @@ -1772,11 +1880,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates - * [tlv] WME + * [4] power capability (optional) + * [28] supported channels (optional) * [tlv] HT capabilities + * [tlv] WME (optional) * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Atheros capabilities (if negotiated) - * [tlv] user-specified ie's + * [tlv] AppIE's (optional) */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), @@ -1786,18 +1896,24 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + 4 + + 2 + 26 + sizeof(struct ieee80211_wme_info) - + 2*sizeof(struct ieee80211_ie_htcap) + 4 + + sizeof(struct ieee80211_ie_htcap) + + 4 + sizeof(struct ieee80211_ie_htcap) + sizeof(struct ieee80211_ath_ie) - + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) + + (vap->iv_appie_wpa != NULL ? + vap->iv_appie_wpa->ie_len : 0) + + (vap->iv_appie_assocreq != NULL ? + vap->iv_appie_assocreq->ie_len : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - KASSERT(ic->ic_opmode == IEEE80211_M_STA, - ("wrong mode %u", ic->ic_opmode)); + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("wrong mode %u", vap->iv_opmode)); capinfo = IEEE80211_CAPINFO_ESS; - if (ic->ic_flags & IEEE80211_F_PRIVACY) + if (vap->iv_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; /* * NB: Some 11a AP's reject the request when @@ -1810,50 +1926,63 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, (ic->ic_caps & IEEE80211_C_SHSLOT)) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) && - (ic->ic_flags & IEEE80211_F_DOTH)) + (vap->iv_flags & IEEE80211_F_DOTH)) capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; *(uint16_t *)frm = htole16(capinfo); frm += 2; - KASSERT(ic->ic_bss->ni_intval != 0, - ("beacon interval is zero!")); + KASSERT(bss->ni_intval != 0, ("beacon interval is zero!")); *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, - ic->ic_bss->ni_intval)); + bss->ni_intval)); frm += 2; if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { - IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); + IEEE80211_ADDR_COPY(frm, bss->ni_bssid); frm += IEEE80211_ADDR_LEN; } frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); frm = ieee80211_add_rates(frm, &ni->ni_rates); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } frm = ieee80211_add_xrates(frm, &ni->ni_rates); - if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) && - ni->ni_htcap_ie != NULL && - ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP) + if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { + frm = ieee80211_add_powercapability(frm, + ic->ic_curchan); + frm = ieee80211_add_supportedchannels(frm, ic); + } + if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) && + ni->ni_ies.htcap_ie != NULL && + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) frm = ieee80211_add_htcap(frm, ni); - if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain */ + } + if ((ic->ic_flags & IEEE80211_F_WME) && + ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); - if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) && - ni->ni_htcap_ie != NULL && - ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR) + if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) && + ni->ni_ies.htcap_ie != NULL && + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) frm = ieee80211_add_htcap_vendor(frm, ni); - if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) + if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) frm = ieee80211_add_ath(frm, - IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), - (ic->ic_flags & IEEE80211_F_WPA) == 0 && + IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), + (vap->iv_flags & IEEE80211_F_WPA) == 0 && ni->ni_authmode != IEEE80211_AUTH_8021X && - ic->ic_def_txkey != IEEE80211_KEYIX_NONE ? - ic->ic_def_txkey : 0x7fff); - if (ic->ic_opt_ie != NULL) { - memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); - frm += ic->ic_opt_ie_len; - } + vap->iv_def_txkey != IEEE80211_KEYIX_NONE ? + vap->iv_def_txkey : 0x7fff); + if (vap->iv_appie_assocreq != NULL) + frm = add_appie(frm, vap->iv_appie_assocreq); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); ieee80211_add_callback(m, ieee80211_tx_mgt_cb, - (void *) ic->ic_state); + (void *) vap->iv_state); break; case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: @@ -1865,10 +1994,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, * [2] association ID * [tlv] supported rates * [tlv] extended supported rates - * [tlv] WME (if enabled and STA enabled) - * [tlv] HT capabilities (standard or vendor OUI) - * [tlv] HT information (standard or vendor OUI) - * [tlv] Atheros capabilities (if enabled and STA enabled) + * [tlv] HT capabilities (standard, if STA enabled) + * [tlv] HT information (standard, if STA enabled) + * [tlv] WME (if configured and STA enabled) + * [tlv] HT capabilities (vendor OUI, if STA enabled) + * [tlv] HT information (vendor OUI, if STA enabled) + * [tlv] Atheros capabilities (if STA enabled) + * [tlv] AppIE's (optional) */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), @@ -1877,15 +2009,17 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + sizeof(uint16_t) + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) - + sizeof(struct ieee80211_wme_param) + sizeof(struct ieee80211_ie_htcap) + 4 + sizeof(struct ieee80211_ie_htinfo) + 4 + + sizeof(struct ieee80211_wme_param) + sizeof(struct ieee80211_ath_ie) + + (vap->iv_appie_assocresp != NULL ? + vap->iv_appie_assocresp->ie_len : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); - capinfo = getcapinfo(ic, ic->ic_curchan); + capinfo = getcapinfo(vap, bss->ni_chan); *(uint16_t *)frm = htole16(capinfo); frm += 2; @@ -1906,23 +2040,25 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, frm = ieee80211_add_htcap(frm, ni); frm = ieee80211_add_htinfo(frm, ni); } - if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) + if ((vap->iv_flags & IEEE80211_F_WME) && + ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); if ((ni->ni_flags & HTFLAGS) == HTFLAGS) { frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } - if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) + if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) frm = ieee80211_add_ath(frm, - IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), + IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), ni->ni_ath_defkeyix); + if (vap->iv_appie_assocresp != NULL) + frm = add_appie(frm, vap->iv_appie_assocresp); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); break; case IEEE80211_FC0_SUBTYPE_DISASSOC: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, - "[%s] send station disassociate (reason %d)\n", - ether_sprintf(ni->ni_macaddr), arg); + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "send station disassociate (reason %d)", arg); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); @@ -1936,17 +2072,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, break; default: - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "[%s] invalid mgmt frame type %u\n", - ether_sprintf(ni->ni_macaddr), type); + IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, + "invalid mgmt frame type %u", type); senderr(EINVAL, is_tx_unknownmgt); /* NOTREACHED */ } - ret = ieee80211_mgmt_output(ic, ni, m, type); - if (ret != 0) - goto bad; - return 0; + return ieee80211_mgmt_output(ni, m, type); bad: ieee80211_free_node(ni); return ret; @@ -1954,19 +2086,283 @@ bad: #undef HTFLAGS } +/* + * Return an mbuf with a probe response frame in it. + * Space is left to prepend and 802.11 header at the + * front but it's left to the caller to fill in. + */ +struct mbuf * +ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) +{ + struct ieee80211vap *vap = bss->ni_vap; + struct ieee80211com *ic = bss->ni_ic; + const struct ieee80211_rateset *rs; + struct mbuf *m; + uint16_t capinfo; + uint8_t *frm; + + /* + * probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] parameter set (FH/DS) + * [tlv] parameter set (IBSS) + * [tlv] country (optional) + * [tlv] RSN (optional) + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] WPA (optional) + * [tlv] WME (optional) + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * [tlv] Atheros capabilities + * [tlv] AppIE's (optional) + */ + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + 8 + + sizeof(uint16_t) + + sizeof(uint16_t) + + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + 7 /* max(7,3) */ + + IEEE80211_COUNTRY_MAX_SIZE + + sizeof(struct ieee80211_ie_wpa) + + 3 + + sizeof(struct ieee80211_csa_ie) + + 3 + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_ie_htcap) + + sizeof(struct ieee80211_ie_htinfo) + + sizeof(struct ieee80211_ie_wpa) + + sizeof(struct ieee80211_wme_param) + + 4 + sizeof(struct ieee80211_ie_htcap) + + 4 + sizeof(struct ieee80211_ie_htinfo) + + sizeof(struct ieee80211_ath_ie) + + (vap->iv_appie_proberesp != NULL ? + vap->iv_appie_proberesp->ie_len : 0) + ); + if (m == NULL) { + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + + memset(frm, 0, 8); /* timestamp should be filled later */ + frm += 8; + *(uint16_t *)frm = htole16(bss->ni_intval); + frm += 2; + capinfo = getcapinfo(vap, bss->ni_chan); + *(uint16_t *)frm = htole16(capinfo); + frm += 2; + + frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen); + rs = ieee80211_get_suprates(ic, bss->ni_chan); + frm = ieee80211_add_rates(frm, rs); + + if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) { + *frm++ = IEEE80211_ELEMID_FHPARMS; + *frm++ = 5; + *frm++ = bss->ni_fhdwell & 0x00ff; + *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff; + *frm++ = IEEE80211_FH_CHANSET( + ieee80211_chan2ieee(ic, bss->ni_chan)); + *frm++ = IEEE80211_FH_CHANPAT( + ieee80211_chan2ieee(ic, bss->ni_chan)); + *frm++ = bss->ni_fhindex; + } else { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan); + } + + if (vap->iv_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + } + if ((vap->iv_flags & IEEE80211_F_DOTH) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) + frm = ieee80211_add_countryie(frm, ic); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } + if (vap->iv_flags & IEEE80211_F_DOTH) { + if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan)) + frm = ieee80211_add_powerconstraint(frm, vap); + if (ic->ic_flags & IEEE80211_F_CSAPENDING) + frm = ieee80211_add_csa(frm, vap); + } + if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan)) + frm = ieee80211_add_erp(frm, ic); + frm = ieee80211_add_xrates(frm, rs); + /* + * NB: legacy 11b clients do not get certain ie's. + * The caller identifies such clients by passing + * a token in legacy to us. Could expand this to be + * any legacy client for stuff like HT ie's. + */ + if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && + legacy != IEEE80211_SEND_LEGACY_11B) { + frm = ieee80211_add_htcap(frm, bss); + frm = ieee80211_add_htinfo(frm, bss); + } + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain? */ + } + if (vap->iv_flags & IEEE80211_F_WME) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && + (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) && + legacy != IEEE80211_SEND_LEGACY_11B) { + frm = ieee80211_add_htcap_vendor(frm, bss); + frm = ieee80211_add_htinfo_vendor(frm, bss); + } + if (bss->ni_ies.ath_ie != NULL && legacy != IEEE80211_SEND_LEGACY_11B) + frm = ieee80211_add_ath(frm, bss->ni_ath_flags, + bss->ni_ath_defkeyix); + if (vap->iv_appie_proberesp != NULL) + frm = add_appie(frm, vap->iv_appie_proberesp); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + return m; +} + +/* + * Send a probe response frame to the specified mac address. + * This does not go through the normal mgt frame api so we + * can specify the destination address and re-use the bss node + * for the sta reference. + */ +int +ieee80211_send_proberesp(struct ieee80211vap *vap, + const uint8_t da[IEEE80211_ADDR_LEN], int legacy) +{ + struct ieee80211_node *bss = vap->iv_bss; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_frame *wh; + struct mbuf *m; + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, + "block %s frame in CAC state", "probe response"); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr), + ieee80211_node_refcnt(bss)+1); + ieee80211_ref_node(bss); + + m = ieee80211_alloc_proberesp(bss, legacy); + if (m == NULL) { + ieee80211_free_node(bss); + return ENOMEM; + } + + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + KASSERT(m != NULL, ("no room for header")); + + wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(bss, wh, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, + vap->iv_myaddr, da, bss->ni_bssid); + /* XXX power management? */ + + M_WME_SETAC(m, WME_AC_BE); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "send probe resp on channel %u to %s%s\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da), + legacy ? " <legacy>" : ""); + IEEE80211_NODE_STAT(bss, tx_mgmt); + + return ic->ic_raw_xmit(bss, m, NULL); +} + +/* + * Allocate and build a RTS (Request To Send) control frame. + */ +struct mbuf * +ieee80211_alloc_rts(struct ieee80211com *ic, + const uint8_t ra[IEEE80211_ADDR_LEN], + const uint8_t ta[IEEE80211_ADDR_LEN], + uint16_t dur) +{ + struct ieee80211_frame_rts *rts; + struct mbuf *m; + + /* XXX honor ic_headroom */ + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m != NULL) { + rts = mtod(m, struct ieee80211_frame_rts *); + rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS; + rts->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)rts->i_dur = htole16(dur); + IEEE80211_ADDR_COPY(rts->i_ra, ra); + IEEE80211_ADDR_COPY(rts->i_ta, ta); + + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); + } + return m; +} + +/* + * Allocate and build a CTS (Clear To Send) control frame. + */ +struct mbuf * +ieee80211_alloc_cts(struct ieee80211com *ic, + const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur) +{ + struct ieee80211_frame_cts *cts; + struct mbuf *m; + + /* XXX honor ic_headroom */ + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m != NULL) { + cts = mtod(m, struct ieee80211_frame_cts *); + cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS; + cts->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)cts->i_dur = htole16(dur); + IEEE80211_ADDR_COPY(cts->i_ra, ra); + + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); + } + return m; +} + static void ieee80211_tx_mgt_timeout(void *arg) { struct ieee80211_node *ni = arg; - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; - if (ic->ic_state != IEEE80211_S_INIT && - (ic->ic_flags & IEEE80211_F_SCAN) == 0) { + if (vap->iv_state != IEEE80211_S_INIT && + (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* * NB: it's safe to specify a timeout as the reason here; * it'll only be used in the right state. */ - ieee80211_new_state(ic, IEEE80211_S_SCAN, + ieee80211_new_state(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT); } } @@ -1974,7 +2370,7 @@ ieee80211_tx_mgt_timeout(void *arg) static void ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) { - struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; enum ieee80211_state ostate = (enum ieee80211_state) arg; /* @@ -1988,27 +2384,20 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) * * XXX what happens if !acked but response shows up before callback? */ - if (ic->ic_state == ostate) - callout_reset(&ic->ic_mgtsend, + if (vap->iv_state == ostate) + callout_reset(&vap->iv_mgtsend, status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, ieee80211_tx_mgt_timeout, ni); } -/* - * Allocate a beacon frame and fillin the appropriate bits. - */ -struct mbuf * -ieee80211_beacon_alloc(struct ieee80211_node *ni, - struct ieee80211_beacon_offsets *bo) +static void +ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, + struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct ieee80211_frame *wh; - struct mbuf *m; - int pktlen; - uint8_t *frm; + struct ieee80211_rateset *rs = &ni->ni_rates; uint16_t capinfo; - struct ieee80211_rateset *rs; /* * beacon frame format @@ -2018,46 +2407,23 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, * [tlv] ssid * [tlv] supported rates * [3] parameter set (DS) + * [8] CF parameter set (optional) * [tlv] parameter set (IBSS/TIM) - * [tlv] country code + * [tlv] country (optional) + * [tlv] RSN parameters + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) * [tlv] extended rate phy (ERP) * [tlv] extended supported rates - * [tlv] WME parameters - * [tlv] WPA/RSN parameters * [tlv] HT capabilities * [tlv] HT information + * XXX Vendor-specific OIDs (e.g. Atheros) + * [tlv] WPA parameters + * [tlv] WME parameters * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Vendor OUI HT information (optional) - * XXX Vendor-specific OIDs (e.g. Atheros) - * NB: we allocate the max space required for the TIM bitmap. + * [tlv] application data (optional) */ - rs = &ni->ni_rates; - pktlen = 8 /* time stamp */ - + sizeof(uint16_t) /* beacon interval */ - + sizeof(uint16_t) /* capabilities */ - + 2 + ni->ni_esslen /* ssid */ - + 2 + IEEE80211_RATE_SIZE /* supported rates */ - + 2 + 1 /* DS parameters */ - + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */ - + sizeof(struct ieee80211_country_ie) /* country code */ - + 2 + 1 /* ERP */ - + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) - + (ic->ic_caps & IEEE80211_C_WME ? /* WME */ - sizeof(struct ieee80211_wme_param) : 0) - + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ - 2*sizeof(struct ieee80211_ie_wpa) : 0) - /* XXX conditional? */ - + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ - + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ - ; - m = ieee80211_getmgtframe(&frm, - ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); - if (m == NULL) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, - "%s: cannot get buf; size %u\n", __func__, pktlen); - ic->ic_stats.is_tx_nobuf++; - return NULL; - } memset(bo, 0, sizeof(*bo)); @@ -2065,68 +2431,169 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, frm += 8; *(uint16_t *)frm = htole16(ni->ni_intval); frm += 2; - capinfo = getcapinfo(ic, ni->ni_chan); + capinfo = getcapinfo(vap, ni->ni_chan); bo->bo_caps = (uint16_t *)frm; *(uint16_t *)frm = htole16(capinfo); frm += 2; *frm++ = IEEE80211_ELEMID_SSID; - if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) { + if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) { *frm++ = ni->ni_esslen; memcpy(frm, ni->ni_essid, ni->ni_esslen); frm += ni->ni_esslen; } else *frm++ = 0; frm = ieee80211_add_rates(frm, rs); - if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) { + if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) { *frm++ = IEEE80211_ELEMID_DSPARMS; *frm++ = 1; - *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + if (ic->ic_flags & IEEE80211_F_PCF) { + bo->bo_cfp = frm; + frm = ieee80211_add_cfparms(frm, ic); } bo->bo_tim = frm; - if (ic->ic_opmode == IEEE80211_M_IBSS) { + if (vap->iv_opmode == IEEE80211_M_IBSS) { *frm++ = IEEE80211_ELEMID_IBSSPARMS; *frm++ = 2; *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ bo->bo_tim_len = 0; - } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) { struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; tie->tim_ie = IEEE80211_ELEMID_TIM; tie->tim_len = 4; /* length */ tie->tim_count = 0; /* DTIM count */ - tie->tim_period = ic->ic_dtim_period; /* DTIM period */ + tie->tim_period = vap->iv_dtim_period; /* DTIM period */ tie->tim_bitctl = 0; /* bitmap control */ tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ frm += sizeof(struct ieee80211_tim_ie); bo->bo_tim_len = 1; } bo->bo_tim_trailer = frm; - if (ic->ic_flags & IEEE80211_F_DOTH) - frm = ieee80211_add_countryie(frm, ic, - ic->ic_countrycode, ic->ic_location); - if (ic->ic_flags & IEEE80211_F_WPA) - frm = ieee80211_add_wpa(frm, ic); - if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) { + if ((vap->iv_flags & IEEE80211_F_DOTH) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) + frm = ieee80211_add_countryie(frm, ic); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain */ + } + if (vap->iv_flags & IEEE80211_F_DOTH) { + if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) + frm = ieee80211_add_powerconstraint(frm, vap); + bo->bo_csa = frm; + if (ic->ic_flags & IEEE80211_F_CSAPENDING) + frm = ieee80211_add_csa(frm, vap); + } else + bo->bo_csa = frm; + if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { bo->bo_erp = frm; frm = ieee80211_add_erp(frm, ic); } frm = ieee80211_add_xrates(frm, rs); - if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) { + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { frm = ieee80211_add_htcap(frm, ni); bo->bo_htinfo = frm; frm = ieee80211_add_htinfo(frm, ni); } - if (ic->ic_flags & IEEE80211_F_WME) { + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain */ + } + if (vap->iv_flags & IEEE80211_F_WME) { bo->bo_wme = frm; frm = ieee80211_add_wme_param(frm, &ic->ic_wme); } - if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) && - (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) { + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && + (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT)) { frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } + if (vap->iv_appie_beacon != NULL) { + bo->bo_appie = frm; + bo->bo_appie_len = vap->iv_appie_beacon->ie_len; + frm = add_appie(frm, vap->iv_appie_beacon); + } bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer; + bo->bo_csa_trailer_len = frm - bo->bo_csa; m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); +} + +/* + * Allocate a beacon frame and fillin the appropriate bits. + */ +struct mbuf * +ieee80211_beacon_alloc(struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_frame *wh; + struct mbuf *m; + int pktlen; + uint8_t *frm; + + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [8] CF parameter set (optional) + * [tlv] parameter set (IBSS/TIM) + * [tlv] country (optional) + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] RSN parameters + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * XXX Vendor-specific OIDs (e.g. Atheros) + * [tlv] WPA parameters + * [tlv] WME parameters + * [tlv] application data (optional) + * NB: we allocate the max space required for the TIM bitmap. + * XXX how big is this? + */ + pktlen = 8 /* time stamp */ + + sizeof(uint16_t) /* beacon interval */ + + sizeof(uint16_t) /* capabilities */ + + 2 + ni->ni_esslen /* ssid */ + + 2 + IEEE80211_RATE_SIZE /* supported rates */ + + 2 + 1 /* DS parameters */ + + 2 + 6 /* CF parameters */ + + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ + + IEEE80211_COUNTRY_MAX_SIZE /* country */ + + 2 + 1 /* power control */ + + sizeof(struct ieee80211_csa_ie) /* CSA */ + + 2 + 1 /* ERP */ + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ + 2*sizeof(struct ieee80211_ie_wpa) : 0) + /* XXX conditional? */ + + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ + + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ + + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ + sizeof(struct ieee80211_wme_param) : 0) + + IEEE80211_MAX_APPIE + ; + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); + if (m == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, + "%s: cannot get buf; size %u\n", __func__, pktlen); + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + ieee80211_beacon_construct(m, frm, bo, ni); M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); KASSERT(m != NULL, ("no space for 802.11 header?")); @@ -2136,7 +2603,7 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni, wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; *(uint16_t *)wh->i_dur = 0; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); *(uint16_t *)wh->i_seq = 0; @@ -2150,16 +2617,46 @@ int ieee80211_beacon_update(struct ieee80211_node *ni, struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) { + struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int len_changed = 0; uint16_t capinfo; - IEEE80211_BEACON_LOCK(ic); + IEEE80211_LOCK(ic); + /* + * Handle 11h channel change when we've reached the count. + * We must recalculate the beacon frame contents to account + * for the new channel. Note we do this only for the first + * vap that reaches this point; subsequent vaps just update + * their beacon state to reflect the recalculated channel. + */ + if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) && + vap->iv_csa_count == ic->ic_csa_count) { + vap->iv_csa_count = 0; + /* + * Effect channel change before reconstructing the beacon + * frame contents as many places reference ni_chan. + */ + if (ic->ic_csa_newchan != NULL) + ieee80211_csa_completeswitch(ic); + /* + * NB: ieee80211_beacon_construct clears all pending + * updates in bo_flags so we don't need to explicitly + * clear IEEE80211_BEACON_CSA. + */ + ieee80211_beacon_construct(m, + mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni); + + /* XXX do WME aggressive mode processing? */ + IEEE80211_UNLOCK(ic); + return 1; /* just assume length changed */ + } + /* XXX faster to recalculate entirely or just changes? */ - capinfo = getcapinfo(ic, ni->ni_chan); + capinfo = getcapinfo(vap, ni->ni_chan); *bo->bo_caps = htole16(capinfo); - if (ic->ic_flags & IEEE80211_F_WME) { + if (vap->iv_flags & IEEE80211_F_WME) { struct ieee80211_wme_state *wme = &ic->ic_wme; /* @@ -2172,11 +2669,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni, if (wme->wme_flags & WME_F_AGGRMODE) { if (wme->wme_hipri_traffic > wme->wme_hipri_switch_thresh) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: traffic %u, disable aggressive mode\n", __func__, wme->wme_hipri_traffic); wme->wme_flags &= ~WME_F_AGGRMODE; - ieee80211_wme_updateparams_locked(ic); + ieee80211_wme_updateparams_locked(vap); wme->wme_hipri_traffic = wme->wme_hipri_switch_hysteresis; } else @@ -2184,11 +2681,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni, } else { if (wme->wme_hipri_traffic <= wme->wme_hipri_switch_thresh) { - IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: traffic %u, enable aggressive mode\n", __func__, wme->wme_hipri_traffic); wme->wme_flags |= WME_F_AGGRMODE; - ieee80211_wme_updateparams_locked(ic); + ieee80211_wme_updateparams_locked(vap); wme->wme_hipri_traffic = 0; } else wme->wme_hipri_traffic = @@ -2200,12 +2697,12 @@ ieee80211_beacon_update(struct ieee80211_node *ni, } } - if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { - ieee80211_ht_update_beacon(ic, bo); + if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { + ieee80211_ht_update_beacon(vap, bo); clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO); } - if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) bo->bo_tim; if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) { @@ -2217,7 +2714,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, * data to make room. Note that we know there is * contiguous space because ieee80211_beacon_allocate * insures there is space in the mbuf to write a - * maximal-size virtual bitmap (based on ic_max_aid). + * maximal-size virtual bitmap (based on iv_max_aid). */ /* * Calculate the bitmap size and offset, copy any @@ -2226,16 +2723,16 @@ ieee80211_beacon_update(struct ieee80211_node *ni, * Note that the tim bitmap must contain at least * one byte and any offset must be even. */ - if (ic->ic_ps_pending != 0) { + if (vap->iv_ps_pending != 0) { timoff = 128; /* impossibly large */ - for (i = 0; i < ic->ic_tim_len; i++) - if (ic->ic_tim_bitmap[i]) { + for (i = 0; i < vap->iv_tim_len; i++) + if (vap->iv_tim_bitmap[i]) { timoff = i &~ 1; break; } KASSERT(timoff != 128, ("tim bitmap empty!")); - for (i = ic->ic_tim_len-1; i >= timoff; i--) - if (ic->ic_tim_bitmap[i]) + for (i = vap->iv_tim_len-1; i >= timoff; i--) + if (vap->iv_tim_bitmap[i]) break; timlen = 1 + (i - timoff); } else { @@ -2250,9 +2747,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni, bo->bo_tim_trailer+adjust, bo->bo_tim_trailer_len); bo->bo_tim_trailer += adjust; - bo->bo_wme += adjust; bo->bo_erp += adjust; bo->bo_htinfo += adjust; + bo->bo_appie += adjust; + bo->bo_wme += adjust; + bo->bo_csa += adjust; bo->bo_tim_len = timlen; /* update information element */ @@ -2260,14 +2759,14 @@ ieee80211_beacon_update(struct ieee80211_node *ni, tie->tim_bitctl = timoff; len_changed = 1; } - memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff, + memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff, bo->bo_tim_len); clrbit(bo->bo_flags, IEEE80211_BEACON_TIM); - IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s: TIM updated, pending %u, off %u, len %u\n", - __func__, ic->ic_ps_pending, timoff, timlen); + __func__, vap->iv_ps_pending, timoff, timlen); } /* count down DTIM period */ if (tie->tim_count == 0) @@ -2279,6 +2778,33 @@ ieee80211_beacon_update(struct ieee80211_node *ni, tie->tim_bitctl |= 1; else tie->tim_bitctl &= ~1; + if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) { + struct ieee80211_csa_ie *csa = + (struct ieee80211_csa_ie *) bo->bo_csa; + + /* + * Insert or update CSA ie. If we're just starting + * to count down to the channel switch then we need + * to insert the CSA ie. Otherwise we just need to + * drop the count. The actual change happens above + * when the vap's count reaches the target count. + */ + if (vap->iv_csa_count == 0) { + memmove(&csa[1], csa, bo->bo_csa_trailer_len); + bo->bo_erp += sizeof(*csa); + bo->bo_wme += sizeof(*csa); + bo->bo_appie += sizeof(*csa); + bo->bo_csa_trailer_len += sizeof(*csa); + bo->bo_tim_trailer_len += sizeof(*csa); + m->m_len += sizeof(*csa); + m->m_pkthdr.len += sizeof(*csa); + + ieee80211_add_csa(bo->bo_csa, vap); + } else + csa->csa_count--; + vap->iv_csa_count++; + /* NB: don't clear IEEE80211_BEACON_CSA */ + } if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { /* * ERP element needs updating. @@ -2287,7 +2813,31 @@ ieee80211_beacon_update(struct ieee80211_node *ni, clrbit(bo->bo_flags, IEEE80211_BEACON_ERP); } } - IEEE80211_BEACON_UNLOCK(ic); + if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) { + const struct ieee80211_appie *aie = vap->iv_appie_beacon; + int aielen; + uint8_t *frm; + + aielen = 0; + if (aie != NULL) + aielen += aie->ie_len; + if (aielen != bo->bo_appie_len) { + /* copy up/down trailer */ + int adjust = aielen - bo->bo_appie_len; + ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, + bo->bo_tim_trailer_len); + bo->bo_tim_trailer += adjust; + bo->bo_appie += adjust; + bo->bo_appie_len = aielen; + + len_changed = 1; + } + frm = bo->bo_appie; + if (aie != NULL) + frm = add_appie(frm, aie); + clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE); + } + IEEE80211_UNLOCK(ic); return len_changed; } |