1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3186904Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 4116742Ssam * All rights reserved. 5116742Ssam * 6116742Ssam * Redistribution and use in source and binary forms, with or without 7116742Ssam * modification, are permitted provided that the following conditions 8116742Ssam * are met: 9116742Ssam * 1. Redistributions of source code must retain the above copyright 10116904Ssam * notice, this list of conditions and the following disclaimer. 11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 12116904Ssam * notice, this list of conditions and the following disclaimer in the 13116904Ssam * documentation and/or other materials provided with the distribution. 14116742Ssam * 15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25116742Ssam */ 26116742Ssam 27116742Ssam#include <sys/cdefs.h> 28116742Ssam__FBSDID("$FreeBSD$"); 29116742Ssam 30116742Ssam#include "opt_inet.h" 31193658Ssam#include "opt_inet6.h" 32178354Ssam#include "opt_wlan.h" 33116742Ssam 34116742Ssam#include <sys/param.h> 35116742Ssam#include <sys/systm.h> 36116742Ssam#include <sys/mbuf.h> 37116742Ssam#include <sys/kernel.h> 38116742Ssam#include <sys/endian.h> 39116742Ssam 40138568Ssam#include <sys/socket.h> 41116742Ssam 42138568Ssam#include <net/bpf.h> 43138568Ssam#include <net/ethernet.h> 44116742Ssam#include <net/if.h> 45138568Ssam#include <net/if_llc.h> 46116742Ssam#include <net/if_media.h> 47138568Ssam#include <net/if_vlan_var.h> 48116742Ssam 49116742Ssam#include <net80211/ieee80211_var.h> 50170530Ssam#include <net80211/ieee80211_regdomain.h> 51190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 52190391Ssam#include <net80211/ieee80211_superg.h> 53190391Ssam#endif 54186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 55186904Ssam#include <net80211/ieee80211_tdma.h> 56186904Ssam#endif 57178354Ssam#include <net80211/ieee80211_wds.h> 58195618Srpaulo#include <net80211/ieee80211_mesh.h> 59116742Ssam 60221022Sbz#if defined(INET) || defined(INET6) 61221022Sbz#include <netinet/in.h> 62221022Sbz#endif 63221022Sbz 64116742Ssam#ifdef INET 65116742Ssam#include <netinet/if_ether.h> 66138568Ssam#include <netinet/in_systm.h> 67138568Ssam#include <netinet/ip.h> 68116742Ssam#endif 69193658Ssam#ifdef INET6 70193658Ssam#include <netinet/ip6.h> 71193658Ssam#endif 72116742Ssam 73193504Srwatson#include <security/mac/mac_framework.h> 74193504Srwatson 75170530Ssam#define ETHER_HEADER_COPY(dst, src) \ 76170530Ssam memcpy(dst, src, sizeof(struct ether_header)) 77170530Ssam 78195618Srpaulo/* unalligned little endian access */ 79195618Srpaulo#define LE_WRITE_2(p, v) do { \ 80195618Srpaulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 81195618Srpaulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 82195618Srpaulo} while (0) 83195618Srpaulo#define LE_WRITE_4(p, v) do { \ 84195618Srpaulo ((uint8_t *)(p))[0] = (v) & 0xff; \ 85195618Srpaulo ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ 86195618Srpaulo ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ 87195618Srpaulo ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ 88195618Srpaulo} while (0) 89195618Srpaulo 90178354Ssamstatic int ieee80211_fragment(struct ieee80211vap *, struct mbuf *, 91170530Ssam u_int hdrsize, u_int ciphdrsize, u_int mtu); 92170530Ssamstatic void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); 93170530Ssam 94138568Ssam#ifdef IEEE80211_DEBUG 95119150Ssam/* 96138568Ssam * Decide if an outbound management frame should be 97138568Ssam * printed when debugging is enabled. This filters some 98138568Ssam * of the less interesting frames that come frequently 99138568Ssam * (e.g. beacons). 100138568Ssam */ 101138568Ssamstatic __inline int 102178354Ssamdoprint(struct ieee80211vap *vap, int subtype) 103138568Ssam{ 104138568Ssam switch (subtype) { 105138568Ssam case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 106178354Ssam return (vap->iv_opmode == IEEE80211_M_IBSS); 107138568Ssam } 108138568Ssam return 1; 109138568Ssam} 110138568Ssam#endif 111138568Ssam 112138568Ssam/* 113178354Ssam * Start method for vap's. All packets from the stack come 114178354Ssam * through here. We handle common processing of the packets 115178354Ssam * before dispatching them to the underlying device. 116178354Ssam */ 117178354Ssamvoid 118178354Ssamieee80211_start(struct ifnet *ifp) 119178354Ssam{ 120178354Ssam#define IS_DWDS(vap) \ 121178354Ssam (vap->iv_opmode == IEEE80211_M_WDS && \ 122178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) 123178354Ssam struct ieee80211vap *vap = ifp->if_softc; 124178354Ssam struct ieee80211com *ic = vap->iv_ic; 125178354Ssam struct ifnet *parent = ic->ic_ifp; 126178354Ssam struct ieee80211_node *ni; 127178354Ssam struct mbuf *m; 128178354Ssam struct ether_header *eh; 129178354Ssam int error; 130178354Ssam 131178354Ssam /* NB: parent must be up and running */ 132178354Ssam if (!IFNET_IS_UP_RUNNING(parent)) { 133178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 134178354Ssam "%s: ignore queue, parent %s not up+running\n", 135178354Ssam __func__, parent->if_xname); 136178354Ssam /* XXX stat */ 137178354Ssam return; 138178354Ssam } 139178354Ssam if (vap->iv_state == IEEE80211_S_SLEEP) { 140178354Ssam /* 141178354Ssam * In power save, wakeup device for transmit. 142178354Ssam */ 143178354Ssam ieee80211_new_state(vap, IEEE80211_S_RUN, 0); 144178354Ssam return; 145178354Ssam } 146178354Ssam /* 147178354Ssam * No data frames go out unless we're running. 148178354Ssam * Note in particular this covers CAC and CSA 149178354Ssam * states (though maybe we should check muting 150178354Ssam * for CSA). 151178354Ssam */ 152178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 153178354Ssam IEEE80211_LOCK(ic); 154178354Ssam /* re-check under the com lock to avoid races */ 155178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 156178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 157178354Ssam "%s: ignore queue, in %s state\n", 158178354Ssam __func__, ieee80211_state_name[vap->iv_state]); 159178354Ssam vap->iv_stats.is_tx_badstate++; 160178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 161178354Ssam IEEE80211_UNLOCK(ic); 162178354Ssam return; 163178354Ssam } 164178354Ssam IEEE80211_UNLOCK(ic); 165178354Ssam } 166178354Ssam for (;;) { 167178354Ssam IFQ_DEQUEUE(&ifp->if_snd, m); 168178354Ssam if (m == NULL) 169178354Ssam break; 170178354Ssam /* 171178354Ssam * Sanitize mbuf flags for net80211 use. We cannot 172193675Ssam * clear M_PWR_SAV or M_MORE_DATA because these may 173193675Ssam * be set for frames that are re-submitted from the 174193675Ssam * power save queue. 175178354Ssam * 176178354Ssam * NB: This must be done before ieee80211_classify as 177178354Ssam * it marks EAPOL in frames with M_EAPOL. 178178354Ssam */ 179193675Ssam m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); 180178354Ssam /* 181178354Ssam * Cancel any background scan. 182178354Ssam */ 183178354Ssam if (ic->ic_flags & IEEE80211_F_SCAN) 184178354Ssam ieee80211_cancel_anyscan(vap); 185178354Ssam /* 186178354Ssam * Find the node for the destination so we can do 187178354Ssam * things like power save and fast frames aggregation. 188178354Ssam * 189178354Ssam * NB: past this point various code assumes the first 190178354Ssam * mbuf has the 802.3 header present (and contiguous). 191178354Ssam */ 192178354Ssam ni = NULL; 193178354Ssam if (m->m_len < sizeof(struct ether_header) && 194178354Ssam (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { 195178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 196178354Ssam "discard frame, %s\n", "m_pullup failed"); 197178354Ssam vap->iv_stats.is_tx_nobuf++; /* XXX */ 198178354Ssam ifp->if_oerrors++; 199178354Ssam continue; 200178354Ssam } 201178354Ssam eh = mtod(m, struct ether_header *); 202178354Ssam if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 203178354Ssam if (IS_DWDS(vap)) { 204178354Ssam /* 205178354Ssam * Only unicast frames from the above go out 206178354Ssam * DWDS vaps; multicast frames are handled by 207178354Ssam * dispatching the frame as it comes through 208178354Ssam * the AP vap (see below). 209178354Ssam */ 210178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, 211178354Ssam eh->ether_dhost, "mcast", "%s", "on DWDS"); 212178354Ssam vap->iv_stats.is_dwds_mcast++; 213178354Ssam m_freem(m); 214178354Ssam continue; 215178354Ssam } 216178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP) { 217178354Ssam /* 218178354Ssam * Spam DWDS vap's w/ multicast traffic. 219178354Ssam */ 220178354Ssam /* XXX only if dwds in use? */ 221178354Ssam ieee80211_dwds_mcast(vap, m); 222178354Ssam } 223178354Ssam } 224195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 225195618Srpaulo if (vap->iv_opmode != IEEE80211_M_MBSS) { 226195618Srpaulo#endif 227195618Srpaulo ni = ieee80211_find_txnode(vap, eh->ether_dhost); 228195618Srpaulo if (ni == NULL) { 229195618Srpaulo /* NB: ieee80211_find_txnode does stat+msg */ 230195618Srpaulo ifp->if_oerrors++; 231195618Srpaulo m_freem(m); 232195618Srpaulo continue; 233195618Srpaulo } 234195618Srpaulo if (ni->ni_associd == 0 && 235195618Srpaulo (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { 236195618Srpaulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, 237195618Srpaulo eh->ether_dhost, NULL, 238195618Srpaulo "sta not associated (type 0x%04x)", 239195618Srpaulo htons(eh->ether_type)); 240195618Srpaulo vap->iv_stats.is_tx_notassoc++; 241195618Srpaulo ifp->if_oerrors++; 242195618Srpaulo m_freem(m); 243195618Srpaulo ieee80211_free_node(ni); 244195618Srpaulo continue; 245195618Srpaulo } 246195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 247195618Srpaulo } else { 248195784Srpaulo if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { 249195784Srpaulo /* 250195784Srpaulo * Proxy station only if configured. 251195784Srpaulo */ 252195784Srpaulo if (!ieee80211_mesh_isproxyena(vap)) { 253195784Srpaulo IEEE80211_DISCARD_MAC(vap, 254195784Srpaulo IEEE80211_MSG_OUTPUT | 255195784Srpaulo IEEE80211_MSG_MESH, 256195784Srpaulo eh->ether_dhost, NULL, 257195784Srpaulo "%s", "proxy not enabled"); 258195784Srpaulo vap->iv_stats.is_mesh_notproxy++; 259195784Srpaulo ifp->if_oerrors++; 260195784Srpaulo m_freem(m); 261195784Srpaulo continue; 262195784Srpaulo } 263195784Srpaulo ieee80211_mesh_proxy_check(vap, eh->ether_shost); 264195784Srpaulo } 265195618Srpaulo ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); 266195618Srpaulo if (ni == NULL) { 267195618Srpaulo /* 268195784Srpaulo * NB: ieee80211_mesh_discover holds/disposes 269195784Srpaulo * frame (e.g. queueing on path discovery). 270195618Srpaulo */ 271195618Srpaulo ifp->if_oerrors++; 272195618Srpaulo continue; 273195618Srpaulo } 274178354Ssam } 275195618Srpaulo#endif 276178354Ssam if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 277178354Ssam (m->m_flags & M_PWR_SAV) == 0) { 278178354Ssam /* 279178354Ssam * Station in power save mode; pass the frame 280178354Ssam * to the 802.11 layer and continue. We'll get 281178354Ssam * the frame back when the time is right. 282178354Ssam * XXX lose WDS vap linkage? 283178354Ssam */ 284184288Ssam (void) ieee80211_pwrsave(ni, m); 285178354Ssam ieee80211_free_node(ni); 286178354Ssam continue; 287178354Ssam } 288178354Ssam /* calculate priority so drivers can find the tx queue */ 289178354Ssam if (ieee80211_classify(ni, m)) { 290178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, 291178354Ssam eh->ether_dhost, NULL, 292178354Ssam "%s", "classification failure"); 293178354Ssam vap->iv_stats.is_tx_classify++; 294178354Ssam ifp->if_oerrors++; 295178354Ssam m_freem(m); 296178354Ssam ieee80211_free_node(ni); 297178354Ssam continue; 298178354Ssam } 299191550Ssam /* 300191550Ssam * Stash the node pointer. Note that we do this after 301191550Ssam * any call to ieee80211_dwds_mcast because that code 302191550Ssam * uses any existing value for rcvif to identify the 303191550Ssam * interface it (might have been) received on. 304191550Ssam */ 305191550Ssam m->m_pkthdr.rcvif = (void *)ni; 306178354Ssam 307190579Ssam BPF_MTAP(ifp, m); /* 802.3 tx */ 308190579Ssam 309191553Ssam /* 310191553Ssam * Check if A-MPDU tx aggregation is setup or if we 311191553Ssam * should try to enable it. The sta must be associated 312191553Ssam * with HT and A-MPDU enabled for use. When the policy 313191553Ssam * routine decides we should enable A-MPDU we issue an 314191553Ssam * ADDBA request and wait for a reply. The frame being 315191553Ssam * encapsulated will go out w/o using A-MPDU, or possibly 316191553Ssam * it might be collected by the driver and held/retransmit. 317191553Ssam * The default ic_ampdu_enable routine handles staggering 318191553Ssam * ADDBA requests in case the receiver NAK's us or we are 319191553Ssam * otherwise unable to establish a BA stream. 320191553Ssam */ 321191553Ssam if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && 322193655Ssam (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) && 323191553Ssam (m->m_flags & M_EAPOL) == 0) { 324191553Ssam const int ac = M_WME_GETAC(m); 325191553Ssam struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; 326191553Ssam 327191553Ssam ieee80211_txampdu_count_packet(tap); 328191553Ssam if (IEEE80211_AMPDU_RUNNING(tap)) { 329191553Ssam /* 330191553Ssam * Operational, mark frame for aggregation. 331191553Ssam * 332191553Ssam * XXX do tx aggregation here 333191553Ssam */ 334191553Ssam m->m_flags |= M_AMPDU_MPDU; 335191553Ssam } else if (!IEEE80211_AMPDU_REQUESTED(tap) && 336191553Ssam ic->ic_ampdu_enable(ni, tap)) { 337191553Ssam /* 338191553Ssam * Not negotiated yet, request service. 339191553Ssam */ 340191553Ssam ieee80211_ampdu_request(ni, tap); 341191553Ssam /* XXX hold frame for reply? */ 342191553Ssam } 343191553Ssam } 344190579Ssam#ifdef IEEE80211_SUPPORT_SUPERG 345191553Ssam else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { 346190579Ssam m = ieee80211_ff_check(ni, m); 347190579Ssam if (m == NULL) { 348190579Ssam /* NB: any ni ref held on stageq */ 349190579Ssam continue; 350190579Ssam } 351190579Ssam } 352190579Ssam#endif /* IEEE80211_SUPPORT_SUPERG */ 353190850Ssam if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { 354190850Ssam /* 355190850Ssam * Encapsulate the packet in prep for transmission. 356190850Ssam */ 357190850Ssam m = ieee80211_encap(vap, ni, m); 358190850Ssam if (m == NULL) { 359190850Ssam /* NB: stat+msg handled in ieee80211_encap */ 360190850Ssam ieee80211_free_node(ni); 361190850Ssam continue; 362190850Ssam } 363190579Ssam } 364178354Ssam 365186658Ssam error = parent->if_transmit(parent, m); 366178354Ssam if (error != 0) { 367178354Ssam /* NB: IFQ_HANDOFF reclaims mbuf */ 368178354Ssam ieee80211_free_node(ni); 369178354Ssam } else { 370178354Ssam ifp->if_opackets++; 371178354Ssam } 372178354Ssam ic->ic_lastdata = ticks; 373178354Ssam } 374178354Ssam#undef IS_DWDS 375178354Ssam} 376178354Ssam 377178354Ssam/* 378178354Ssam * 802.11 output routine. This is (currently) used only to 379178354Ssam * connect bpf write calls to the 802.11 layer for injecting 380191538Ssam * raw 802.11 frames. 381178354Ssam */ 382178354Ssamint 383178354Ssamieee80211_output(struct ifnet *ifp, struct mbuf *m, 384191148Skmacy struct sockaddr *dst, struct route *ro) 385178354Ssam{ 386178354Ssam#define senderr(e) do { error = (e); goto bad;} while (0) 387178354Ssam struct ieee80211_node *ni = NULL; 388195849Ssam struct ieee80211vap *vap; 389178354Ssam struct ieee80211_frame *wh; 390178354Ssam int error; 391178354Ssam 392195849Ssam if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { 393195849Ssam /* 394195849Ssam * Short-circuit requests if the vap is marked OACTIVE 395195849Ssam * as this can happen because a packet came down through 396195849Ssam * ieee80211_start before the vap entered RUN state in 397195849Ssam * which case it's ok to just drop the frame. This 398195849Ssam * should not be necessary but callers of if_output don't 399195849Ssam * check OACTIVE. 400195849Ssam */ 401195849Ssam senderr(ENETDOWN); 402195849Ssam } 403195849Ssam vap = ifp->if_softc; 404178354Ssam /* 405178354Ssam * Hand to the 802.3 code if not tagged as 406178354Ssam * a raw 802.11 frame. 407178354Ssam */ 408178354Ssam if (dst->sa_family != AF_IEEE80211) 409191148Skmacy return vap->iv_output(ifp, m, dst, ro); 410178354Ssam#ifdef MAC 411193504Srwatson error = mac_ifnet_check_transmit(ifp, m); 412178354Ssam if (error) 413178354Ssam senderr(error); 414178354Ssam#endif 415178354Ssam if (ifp->if_flags & IFF_MONITOR) 416178354Ssam senderr(ENETDOWN); 417178354Ssam if (!IFNET_IS_UP_RUNNING(ifp)) 418178354Ssam senderr(ENETDOWN); 419178354Ssam if (vap->iv_state == IEEE80211_S_CAC) { 420178354Ssam IEEE80211_DPRINTF(vap, 421178354Ssam IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, 422178354Ssam "block %s frame in CAC state\n", "raw data"); 423178354Ssam vap->iv_stats.is_tx_badstate++; 424178354Ssam senderr(EIO); /* XXX */ 425219604Sbschmidt } else if (vap->iv_state == IEEE80211_S_SCAN) 426219604Sbschmidt senderr(EIO); 427178354Ssam /* XXX bypass bridge, pfil, carp, etc. */ 428178354Ssam 429178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) 430178354Ssam senderr(EIO); /* XXX */ 431178354Ssam wh = mtod(m, struct ieee80211_frame *); 432178354Ssam if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 433178354Ssam IEEE80211_FC0_VERSION_0) 434178354Ssam senderr(EIO); /* XXX */ 435178354Ssam 436178354Ssam /* locate destination node */ 437178354Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 438178354Ssam case IEEE80211_FC1_DIR_NODS: 439178354Ssam case IEEE80211_FC1_DIR_FROMDS: 440178354Ssam ni = ieee80211_find_txnode(vap, wh->i_addr1); 441178354Ssam break; 442178354Ssam case IEEE80211_FC1_DIR_TODS: 443178354Ssam case IEEE80211_FC1_DIR_DSTODS: 444178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) 445178354Ssam senderr(EIO); /* XXX */ 446178354Ssam ni = ieee80211_find_txnode(vap, wh->i_addr3); 447178354Ssam break; 448178354Ssam default: 449178354Ssam senderr(EIO); /* XXX */ 450178354Ssam } 451178354Ssam if (ni == NULL) { 452178354Ssam /* 453178354Ssam * Permit packets w/ bpf params through regardless 454178354Ssam * (see below about sa_len). 455178354Ssam */ 456178354Ssam if (dst->sa_len == 0) 457178354Ssam senderr(EHOSTUNREACH); 458178354Ssam ni = ieee80211_ref_node(vap->iv_bss); 459178354Ssam } 460178354Ssam 461178354Ssam /* 462178354Ssam * Sanitize mbuf for net80211 flags leaked from above. 463178354Ssam * 464178354Ssam * NB: This must be done before ieee80211_classify as 465178354Ssam * it marks EAPOL in frames with M_EAPOL. 466178354Ssam */ 467178354Ssam m->m_flags &= ~M_80211_TX; 468178354Ssam 469178354Ssam /* calculate priority so drivers can find the tx queue */ 470178354Ssam /* XXX assumes an 802.3 frame */ 471178354Ssam if (ieee80211_classify(ni, m)) 472178354Ssam senderr(EIO); /* XXX */ 473178354Ssam 474195845Ssam ifp->if_opackets++; 475191536Ssam IEEE80211_NODE_STAT(ni, tx_data); 476191536Ssam if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 477191536Ssam IEEE80211_NODE_STAT(ni, tx_mcast); 478191536Ssam m->m_flags |= M_MCAST; 479191536Ssam } else 480191536Ssam IEEE80211_NODE_STAT(ni, tx_ucast); 481191536Ssam /* NB: ieee80211_encap does not include 802.11 header */ 482191536Ssam IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len); 483191536Ssam 484178354Ssam /* 485178354Ssam * NB: DLT_IEEE802_11_RADIO identifies the parameters are 486178354Ssam * present by setting the sa_len field of the sockaddr (yes, 487178354Ssam * this is a hack). 488178354Ssam * NB: we assume sa_data is suitably aligned to cast. 489178354Ssam */ 490178354Ssam return vap->iv_ic->ic_raw_xmit(ni, m, 491178354Ssam (const struct ieee80211_bpf_params *)(dst->sa_len ? 492178354Ssam dst->sa_data : NULL)); 493178354Ssambad: 494178354Ssam if (m != NULL) 495178354Ssam m_freem(m); 496178354Ssam if (ni != NULL) 497178354Ssam ieee80211_free_node(ni); 498195845Ssam ifp->if_oerrors++; 499178354Ssam return error; 500178354Ssam#undef senderr 501178354Ssam} 502178354Ssam 503178354Ssam/* 504148314Ssam * Set the direction field and address fields of an outgoing 505184285Ssam * frame. Note this should be called early on in constructing 506184285Ssam * a frame as it sets i_fc[1]; other bits can then be or'd in. 507148314Ssam */ 508195618Srpaulovoid 509178354Ssamieee80211_send_setup( 510148314Ssam struct ieee80211_node *ni, 511191544Ssam struct mbuf *m, 512184282Ssam int type, int tid, 513170530Ssam const uint8_t sa[IEEE80211_ADDR_LEN], 514170530Ssam const uint8_t da[IEEE80211_ADDR_LEN], 515170530Ssam const uint8_t bssid[IEEE80211_ADDR_LEN]) 516148314Ssam{ 517148314Ssam#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) 518195618Srpaulo struct ieee80211vap *vap = ni->ni_vap; 519222682Sbschmidt struct ieee80211_tx_ampdu *tap; 520191544Ssam struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); 521191544Ssam ieee80211_seq seqno; 522148314Ssam 523148314Ssam wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; 524148314Ssam if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { 525178354Ssam switch (vap->iv_opmode) { 526148314Ssam case IEEE80211_M_STA: 527148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 528148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, bssid); 529148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, sa); 530148314Ssam IEEE80211_ADDR_COPY(wh->i_addr3, da); 531148314Ssam break; 532148314Ssam case IEEE80211_M_IBSS: 533148314Ssam case IEEE80211_M_AHDEMO: 534148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 535148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, da); 536148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, sa); 537148314Ssam IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 538148314Ssam break; 539148314Ssam case IEEE80211_M_HOSTAP: 540148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 541148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, da); 542148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, bssid); 543148314Ssam IEEE80211_ADDR_COPY(wh->i_addr3, sa); 544148314Ssam break; 545170530Ssam case IEEE80211_M_WDS: 546170530Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 547178354Ssam IEEE80211_ADDR_COPY(wh->i_addr1, da); 548178354Ssam IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 549170530Ssam IEEE80211_ADDR_COPY(wh->i_addr3, da); 550170530Ssam IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); 551170530Ssam break; 552195618Srpaulo case IEEE80211_M_MBSS: 553195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 554195618Srpaulo /* XXX add support for proxied addresses */ 555195618Srpaulo if (IEEE80211_IS_MULTICAST(da)) { 556195618Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 557195618Srpaulo /* XXX next hop */ 558195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, da); 559195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, 560195618Srpaulo vap->iv_myaddr); 561195618Srpaulo } else { 562195618Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 563195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, da); 564195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, 565195618Srpaulo vap->iv_myaddr); 566195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, da); 567195618Srpaulo IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); 568195618Srpaulo } 569195618Srpaulo#endif 570195618Srpaulo break; 571148314Ssam case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ 572148314Ssam break; 573148314Ssam } 574148314Ssam } else { 575148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 576148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, da); 577148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, sa); 578195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 579195618Srpaulo if (vap->iv_opmode == IEEE80211_M_MBSS) 580195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, sa); 581195618Srpaulo else 582195618Srpaulo#endif 583195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 584148314Ssam } 585170530Ssam *(uint16_t *)&wh->i_dur[0] = 0; 586191544Ssam 587222682Sbschmidt tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)]; 588222682Sbschmidt if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) 589222682Sbschmidt m->m_flags |= M_AMPDU_MPDU; 590222682Sbschmidt else { 591222682Sbschmidt seqno = ni->ni_txseqs[tid]++; 592222682Sbschmidt *(uint16_t *)&wh->i_seq[0] = 593222682Sbschmidt htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 594222682Sbschmidt M_SEQNO_SET(m, seqno); 595222682Sbschmidt } 596191544Ssam 597191544Ssam if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 598191544Ssam m->m_flags |= M_MCAST; 599148314Ssam#undef WH4 600148314Ssam} 601148314Ssam 602148314Ssam/* 603119150Ssam * Send a management frame to the specified node. The node pointer 604119150Ssam * must have a reference as the pointer will be passed to the driver 605119150Ssam * and potentially held for a long time. If the frame is successfully 606119150Ssam * dispatched to the driver, then it is responsible for freeing the 607178354Ssam * reference (and potentially free'ing up any associated storage); 608178354Ssam * otherwise deal with reclaiming any reference (on error). 609119150Ssam */ 610170530Ssamint 611184282Ssamieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type, 612184282Ssam struct ieee80211_bpf_params *params) 613116742Ssam{ 614178354Ssam struct ieee80211vap *vap = ni->ni_vap; 615178354Ssam struct ieee80211com *ic = ni->ni_ic; 616116742Ssam struct ieee80211_frame *wh; 617116742Ssam 618119150Ssam KASSERT(ni != NULL, ("null node")); 619116742Ssam 620178354Ssam if (vap->iv_state == IEEE80211_S_CAC) { 621178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, 622178354Ssam ni, "block %s frame in CAC state", 623178354Ssam ieee80211_mgt_subtype_name[ 624178354Ssam (type & IEEE80211_FC0_SUBTYPE_MASK) >> 625178354Ssam IEEE80211_FC0_SUBTYPE_SHIFT]); 626178354Ssam vap->iv_stats.is_tx_badstate++; 627178354Ssam ieee80211_free_node(ni); 628178354Ssam m_freem(m); 629178354Ssam return EIO; /* XXX */ 630178354Ssam } 631178354Ssam 632116742Ssam M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 633178354Ssam if (m == NULL) { 634178354Ssam ieee80211_free_node(ni); 635116742Ssam return ENOMEM; 636178354Ssam } 637119150Ssam 638116742Ssam wh = mtod(m, struct ieee80211_frame *); 639191544Ssam ieee80211_send_setup(ni, m, 640184282Ssam IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID, 641184282Ssam vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); 642184282Ssam if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { 643178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, 644178354Ssam "encrypting frame (%s)", __func__); 645138568Ssam wh->i_fc[1] |= IEEE80211_FC1_WEP; 646138568Ssam } 647184286Ssam m->m_flags |= M_ENCAP; /* mark encapsulated */ 648184282Ssam 649184282Ssam KASSERT(type != IEEE80211_FC0_SUBTYPE_PROBE_RESP, ("probe response?")); 650184282Ssam M_WME_SETAC(m, params->ibp_pri); 651184282Ssam 652116742Ssam#ifdef IEEE80211_DEBUG 653138568Ssam /* avoid printing too many frames */ 654178354Ssam if ((ieee80211_msg_debug(vap) && doprint(vap, type)) || 655178354Ssam ieee80211_msg_dumppkts(vap)) { 656138568Ssam printf("[%s] send %s on channel %u\n", 657138568Ssam ether_sprintf(wh->i_addr1), 658138568Ssam ieee80211_mgt_subtype_name[ 659138568Ssam (type & IEEE80211_FC0_SUBTYPE_MASK) >> 660138568Ssam IEEE80211_FC0_SUBTYPE_SHIFT], 661148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan)); 662138568Ssam } 663116742Ssam#endif 664138568Ssam IEEE80211_NODE_STAT(ni, tx_mgmt); 665170530Ssam 666184282Ssam return ic->ic_raw_xmit(ni, m, params); 667116742Ssam} 668116742Ssam 669119150Ssam/* 670184283Ssam * Send a null data frame to the specified node. If the station 671184283Ssam * is setup for QoS then a QoS Null Data frame is constructed. 672184283Ssam * If this is a WDS station then a 4-address frame is constructed. 673148582Ssam * 674148582Ssam * NB: the caller is assumed to have setup a node reference 675148582Ssam * for use; this is necessary to deal with a race condition 676178354Ssam * when probing for inactive stations. Like ieee80211_mgmt_output 677178354Ssam * we must cleanup any node reference on error; however we 678178354Ssam * can safely just unref it as we know it will never be the 679178354Ssam * last reference to the node. 680119150Ssam */ 681138568Ssamint 682148301Ssamieee80211_send_nulldata(struct ieee80211_node *ni) 683138568Ssam{ 684178354Ssam struct ieee80211vap *vap = ni->ni_vap; 685148301Ssam struct ieee80211com *ic = ni->ni_ic; 686138568Ssam struct mbuf *m; 687138568Ssam struct ieee80211_frame *wh; 688184283Ssam int hdrlen; 689184283Ssam uint8_t *frm; 690138568Ssam 691178354Ssam if (vap->iv_state == IEEE80211_S_CAC) { 692178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, 693178354Ssam ni, "block %s frame in CAC state", "null data"); 694178354Ssam ieee80211_unref_node(&ni); 695178354Ssam vap->iv_stats.is_tx_badstate++; 696178354Ssam return EIO; /* XXX */ 697178354Ssam } 698178354Ssam 699184283Ssam if (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) 700184283Ssam hdrlen = sizeof(struct ieee80211_qosframe); 701184283Ssam else 702184283Ssam hdrlen = sizeof(struct ieee80211_frame); 703184283Ssam /* NB: only WDS vap's get 4-address frames */ 704184283Ssam if (vap->iv_opmode == IEEE80211_M_WDS) 705184283Ssam hdrlen += IEEE80211_ADDR_LEN; 706184283Ssam if (ic->ic_flags & IEEE80211_F_DATAPAD) 707184283Ssam hdrlen = roundup(hdrlen, sizeof(uint32_t)); 708184283Ssam 709184283Ssam m = ieee80211_getmgtframe(&frm, ic->ic_headroom + hdrlen, 0); 710138568Ssam if (m == NULL) { 711138568Ssam /* XXX debug msg */ 712170530Ssam ieee80211_unref_node(&ni); 713178354Ssam vap->iv_stats.is_tx_nobuf++; 714138568Ssam return ENOMEM; 715138568Ssam } 716184283Ssam KASSERT(M_LEADINGSPACE(m) >= hdrlen, 717184283Ssam ("leading space %zd", M_LEADINGSPACE(m))); 718184283Ssam M_PREPEND(m, hdrlen, M_DONTWAIT); 719184283Ssam if (m == NULL) { 720184283Ssam /* NB: cannot happen */ 721184283Ssam ieee80211_free_node(ni); 722184283Ssam return ENOMEM; 723184283Ssam } 724138568Ssam 725184283Ssam wh = mtod(m, struct ieee80211_frame *); /* NB: a little lie */ 726184283Ssam if (ni->ni_flags & IEEE80211_NODE_QOS) { 727184283Ssam const int tid = WME_AC_TO_TID(WME_AC_BE); 728184283Ssam uint8_t *qos; 729184283Ssam 730191544Ssam ieee80211_send_setup(ni, m, 731184283Ssam IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS_NULL, 732184283Ssam tid, vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); 733184283Ssam 734184283Ssam if (vap->iv_opmode == IEEE80211_M_WDS) 735184283Ssam qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; 736184283Ssam else 737184283Ssam qos = ((struct ieee80211_qosframe *) wh)->i_qos; 738184283Ssam qos[0] = tid & IEEE80211_QOS_TID; 739184283Ssam if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[WME_AC_BE].wmep_noackPolicy) 740184283Ssam qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; 741184283Ssam qos[1] = 0; 742184283Ssam } else { 743191544Ssam ieee80211_send_setup(ni, m, 744184283Ssam IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, 745184283Ssam IEEE80211_NONQOS_TID, 746184283Ssam vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); 747184283Ssam } 748178354Ssam if (vap->iv_opmode != IEEE80211_M_WDS) { 749178354Ssam /* NB: power management bit is never sent by an AP */ 750178354Ssam if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 751178354Ssam vap->iv_opmode != IEEE80211_M_HOSTAP) 752178354Ssam wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; 753178354Ssam } 754184283Ssam m->m_len = m->m_pkthdr.len = hdrlen; 755184286Ssam m->m_flags |= M_ENCAP; /* mark encapsulated */ 756184283Ssam 757172231Ssam M_WME_SETAC(m, WME_AC_BE); 758138568Ssam 759138568Ssam IEEE80211_NODE_STAT(ni, tx_data); 760138568Ssam 761178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni, 762184283Ssam "send %snull data frame on channel %u, pwr mgt %s", 763184283Ssam ni->ni_flags & IEEE80211_NODE_QOS ? "QoS " : "", 764148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 765148314Ssam wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); 766148314Ssam 767178354Ssam return ic->ic_raw_xmit(ni, m, NULL); 768138568Ssam} 769138568Ssam 770138568Ssam/* 771138568Ssam * Assign priority to a frame based on any vlan tag assigned 772138568Ssam * to the station and/or any Diffserv setting in an IP header. 773138568Ssam * Finally, if an ACM policy is setup (in station mode) it's 774138568Ssam * applied. 775138568Ssam */ 776138568Ssamint 777178354Ssamieee80211_classify(struct ieee80211_node *ni, struct mbuf *m) 778138568Ssam{ 779178354Ssam const struct ether_header *eh = mtod(m, struct ether_header *); 780138568Ssam int v_wme_ac, d_wme_ac, ac; 781138568Ssam 782178354Ssam /* 783178354Ssam * Always promote PAE/EAPOL frames to high priority. 784178354Ssam */ 785178354Ssam if (eh->ether_type == htons(ETHERTYPE_PAE)) { 786178354Ssam /* NB: mark so others don't need to check header */ 787178354Ssam m->m_flags |= M_EAPOL; 788178354Ssam ac = WME_AC_VO; 789178354Ssam goto done; 790178354Ssam } 791178354Ssam /* 792178354Ssam * Non-qos traffic goes to BE. 793178354Ssam */ 794138568Ssam if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { 795138568Ssam ac = WME_AC_BE; 796138568Ssam goto done; 797138568Ssam } 798138568Ssam 799138568Ssam /* 800138568Ssam * If node has a vlan tag then all traffic 801138568Ssam * to it must have a matching tag. 802138568Ssam */ 803138568Ssam v_wme_ac = 0; 804138568Ssam if (ni->ni_vlan != 0) { 805162375Sandre if ((m->m_flags & M_VLANTAG) == 0) { 806138568Ssam IEEE80211_NODE_STAT(ni, tx_novlantag); 807138568Ssam return 1; 808138568Ssam } 809162375Sandre if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != 810138568Ssam EVL_VLANOFTAG(ni->ni_vlan)) { 811138568Ssam IEEE80211_NODE_STAT(ni, tx_vlanmismatch); 812138568Ssam return 1; 813138568Ssam } 814138568Ssam /* map vlan priority to AC */ 815173867Ssam v_wme_ac = TID_TO_WME_AC(EVL_PRIOFTAG(ni->ni_vlan)); 816138568Ssam } 817138568Ssam 818193658Ssam /* XXX m_copydata may be too slow for fast path */ 819138568Ssam#ifdef INET 820138568Ssam if (eh->ether_type == htons(ETHERTYPE_IP)) { 821173867Ssam uint8_t tos; 822138568Ssam /* 823173867Ssam * IP frame, map the DSCP bits from the TOS field. 824138568Ssam */ 825173867Ssam /* NB: ip header may not be in first mbuf */ 826173867Ssam m_copydata(m, sizeof(struct ether_header) + 827173867Ssam offsetof(struct ip, ip_tos), sizeof(tos), &tos); 828173867Ssam tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ 829173867Ssam d_wme_ac = TID_TO_WME_AC(tos); 830138568Ssam } else { 831138568Ssam#endif /* INET */ 832193658Ssam#ifdef INET6 833193658Ssam if (eh->ether_type == htons(ETHERTYPE_IPV6)) { 834193658Ssam uint32_t flow; 835193658Ssam uint8_t tos; 836193658Ssam /* 837231304Sbz * IPv6 frame, map the DSCP bits from the traffic class field. 838193658Ssam */ 839193658Ssam m_copydata(m, sizeof(struct ether_header) + 840193658Ssam offsetof(struct ip6_hdr, ip6_flow), sizeof(flow), 841193658Ssam (caddr_t) &flow); 842193658Ssam tos = (uint8_t)(ntohl(flow) >> 20); 843193658Ssam tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ 844193658Ssam d_wme_ac = TID_TO_WME_AC(tos); 845193658Ssam } else { 846193658Ssam#endif /* INET6 */ 847138568Ssam d_wme_ac = WME_AC_BE; 848193658Ssam#ifdef INET6 849193658Ssam } 850193658Ssam#endif 851138568Ssam#ifdef INET 852138568Ssam } 853138568Ssam#endif 854138568Ssam /* 855138568Ssam * Use highest priority AC. 856138568Ssam */ 857138568Ssam if (v_wme_ac > d_wme_ac) 858138568Ssam ac = v_wme_ac; 859138568Ssam else 860138568Ssam ac = d_wme_ac; 861138568Ssam 862138568Ssam /* 863138568Ssam * Apply ACM policy. 864138568Ssam */ 865178354Ssam if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) { 866138568Ssam static const int acmap[4] = { 867138568Ssam WME_AC_BK, /* WME_AC_BE */ 868138568Ssam WME_AC_BK, /* WME_AC_BK */ 869138568Ssam WME_AC_BE, /* WME_AC_VI */ 870138568Ssam WME_AC_VI, /* WME_AC_VO */ 871138568Ssam }; 872178354Ssam struct ieee80211com *ic = ni->ni_ic; 873178354Ssam 874138568Ssam while (ac != WME_AC_BK && 875138568Ssam ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) 876138568Ssam ac = acmap[ac]; 877138568Ssam } 878138568Ssamdone: 879138568Ssam M_WME_SETAC(m, ac); 880138568Ssam return 0; 881138568Ssam} 882138568Ssam 883138568Ssam/* 884139527Ssam * Insure there is sufficient contiguous space to encapsulate the 885139527Ssam * 802.11 data frame. If room isn't already there, arrange for it. 886139527Ssam * Drivers and cipher modules assume we have done the necessary work 887139527Ssam * and fail rudely if they don't find the space they need. 888139527Ssam */ 889190391Ssamstruct mbuf * 890178354Ssamieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize, 891139527Ssam struct ieee80211_key *key, struct mbuf *m) 892139527Ssam{ 893164805Ssam#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) 894178354Ssam int needed_space = vap->iv_ic->ic_headroom + hdrsize; 895139527Ssam 896139527Ssam if (key != NULL) { 897139527Ssam /* XXX belongs in crypto code? */ 898139527Ssam needed_space += key->wk_cipher->ic_header; 899139527Ssam /* XXX frags */ 900156758Ssam /* 901156758Ssam * When crypto is being done in the host we must insure 902156758Ssam * the data are writable for the cipher routines; clone 903156758Ssam * a writable mbuf chain. 904156758Ssam * XXX handle SWMIC specially 905156758Ssam */ 906179394Ssam if (key->wk_flags & (IEEE80211_KEY_SWENCRYPT|IEEE80211_KEY_SWENMIC)) { 907156758Ssam m = m_unshare(m, M_NOWAIT); 908156758Ssam if (m == NULL) { 909178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 910156758Ssam "%s: cannot get writable mbuf\n", __func__); 911178354Ssam vap->iv_stats.is_tx_nobuf++; /* XXX new stat */ 912156758Ssam return NULL; 913156758Ssam } 914156758Ssam } 915139527Ssam } 916139527Ssam /* 917139527Ssam * We know we are called just before stripping an Ethernet 918139527Ssam * header and prepending an LLC header. This means we know 919139527Ssam * there will be 920164805Ssam * sizeof(struct ether_header) - sizeof(struct llc) 921139527Ssam * bytes recovered to which we need additional space for the 922139527Ssam * 802.11 header and any crypto header. 923139527Ssam */ 924139527Ssam /* XXX check trailing space and copy instead? */ 925139527Ssam if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { 926139527Ssam struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); 927139527Ssam if (n == NULL) { 928178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 929139527Ssam "%s: cannot expand storage\n", __func__); 930178354Ssam vap->iv_stats.is_tx_nobuf++; 931139527Ssam m_freem(m); 932139527Ssam return NULL; 933139527Ssam } 934139527Ssam KASSERT(needed_space <= MHLEN, 935139527Ssam ("not enough room, need %u got %zu\n", needed_space, MHLEN)); 936139527Ssam /* 937139527Ssam * Setup new mbuf to have leading space to prepend the 938139527Ssam * 802.11 header and any crypto header bits that are 939139527Ssam * required (the latter are added when the driver calls 940139527Ssam * back to ieee80211_crypto_encap to do crypto encapsulation). 941139527Ssam */ 942139527Ssam /* NB: must be first 'cuz it clobbers m_data */ 943139527Ssam m_move_pkthdr(n, m); 944139527Ssam n->m_len = 0; /* NB: m_gethdr does not set */ 945139527Ssam n->m_data += needed_space; 946139527Ssam /* 947139527Ssam * Pull up Ethernet header to create the expected layout. 948139527Ssam * We could use m_pullup but that's overkill (i.e. we don't 949139527Ssam * need the actual data) and it cannot fail so do it inline 950139527Ssam * for speed. 951139527Ssam */ 952139527Ssam /* NB: struct ether_header is known to be contiguous */ 953139527Ssam n->m_len += sizeof(struct ether_header); 954139527Ssam m->m_len -= sizeof(struct ether_header); 955139527Ssam m->m_data += sizeof(struct ether_header); 956139527Ssam /* 957139527Ssam * Replace the head of the chain. 958139527Ssam */ 959139527Ssam n->m_next = m; 960139527Ssam m = n; 961139527Ssam } 962139527Ssam return m; 963139527Ssam#undef TO_BE_RECLAIMED 964139527Ssam} 965139527Ssam 966139527Ssam/* 967139527Ssam * Return the transmit key to use in sending a unicast frame. 968139527Ssam * If a unicast key is set we use that. When no unicast key is set 969139527Ssam * we fall back to the default transmit key. 970138568Ssam */ 971138568Ssamstatic __inline struct ieee80211_key * 972178354Ssamieee80211_crypto_getucastkey(struct ieee80211vap *vap, 973178354Ssam struct ieee80211_node *ni) 974138568Ssam{ 975167432Ssam if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { 976178354Ssam if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || 977178354Ssam IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) 978138568Ssam return NULL; 979178354Ssam return &vap->iv_nw_keys[vap->iv_def_txkey]; 980138568Ssam } else { 981138568Ssam return &ni->ni_ucastkey; 982138568Ssam } 983138568Ssam} 984138568Ssam 985138568Ssam/* 986139527Ssam * Return the transmit key to use in sending a multicast frame. 987139527Ssam * Multicast traffic always uses the group key which is installed as 988139527Ssam * the default tx key. 989139527Ssam */ 990139527Ssamstatic __inline struct ieee80211_key * 991178354Ssamieee80211_crypto_getmcastkey(struct ieee80211vap *vap, 992178354Ssam struct ieee80211_node *ni) 993139527Ssam{ 994178354Ssam if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || 995178354Ssam IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) 996139527Ssam return NULL; 997178354Ssam return &vap->iv_nw_keys[vap->iv_def_txkey]; 998139527Ssam} 999139527Ssam 1000139527Ssam/* 1001138568Ssam * Encapsulate an outbound data frame. The mbuf chain is updated. 1002138568Ssam * If an error is encountered NULL is returned. The caller is required 1003138568Ssam * to provide a node reference and pullup the ethernet header in the 1004138568Ssam * first mbuf. 1005178354Ssam * 1006178354Ssam * NB: Packet is assumed to be processed by ieee80211_classify which 1007178354Ssam * marked EAPOL frames w/ M_EAPOL. 1008138568Ssam */ 1009116742Ssamstruct mbuf * 1010190579Ssamieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, 1011190579Ssam struct mbuf *m) 1012116742Ssam{ 1013178354Ssam#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh)) 1014178354Ssam struct ieee80211com *ic = ni->ni_ic; 1015195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 1016195618Srpaulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 1017195784Srpaulo struct ieee80211_meshcntl_ae10 *mc; 1018195618Srpaulo#endif 1019116742Ssam struct ether_header eh; 1020116742Ssam struct ieee80211_frame *wh; 1021138568Ssam struct ieee80211_key *key; 1022116742Ssam struct llc *llc; 1023190391Ssam int hdrsize, hdrspace, datalen, addqos, txfrag, is4addr; 1024191544Ssam ieee80211_seq seqno; 1025195618Srpaulo int meshhdrsize, meshae; 1026195618Srpaulo uint8_t *qos; 1027116742Ssam 1028170530Ssam /* 1029170530Ssam * Copy existing Ethernet header to a safe place. The 1030170530Ssam * rest of the code assumes it's ok to strip it when 1031170530Ssam * reorganizing state for the final encapsulation. 1032170530Ssam */ 1033138568Ssam KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); 1034178354Ssam ETHER_HEADER_COPY(&eh, mtod(m, caddr_t)); 1035116742Ssam 1036138568Ssam /* 1037138568Ssam * Insure space for additional headers. First identify 1038138568Ssam * transmit key to use in calculating any buffer adjustments 1039138568Ssam * required. This is also used below to do privacy 1040138568Ssam * encapsulation work. Then calculate the 802.11 header 1041138568Ssam * size and any padding required by the driver. 1042138568Ssam * 1043138568Ssam * Note key may be NULL if we fall back to the default 1044138568Ssam * transmit key and that is not set. In that case the 1045138568Ssam * buffer may not be expanded as needed by the cipher 1046138568Ssam * routines, but they will/should discard it. 1047138568Ssam */ 1048178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) { 1049178354Ssam if (vap->iv_opmode == IEEE80211_M_STA || 1050178354Ssam !IEEE80211_IS_MULTICAST(eh.ether_dhost) || 1051178354Ssam (vap->iv_opmode == IEEE80211_M_WDS && 1052178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) 1053178354Ssam key = ieee80211_crypto_getucastkey(vap, ni); 1054139527Ssam else 1055178354Ssam key = ieee80211_crypto_getmcastkey(vap, ni); 1056178354Ssam if (key == NULL && (m->m_flags & M_EAPOL) == 0) { 1057178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, 1058178354Ssam eh.ether_dhost, 1059178354Ssam "no default transmit key (%s) deftxkey %u", 1060178354Ssam __func__, vap->iv_def_txkey); 1061178354Ssam vap->iv_stats.is_tx_nodefkey++; 1062171950Ssam goto bad; 1063138568Ssam } 1064138568Ssam } else 1065138568Ssam key = NULL; 1066139527Ssam /* 1067139527Ssam * XXX Some ap's don't handle QoS-encapsulated EAPOL 1068139527Ssam * frames so suppress use. This may be an issue if other 1069139527Ssam * ap's require all data frames to be QoS-encapsulated 1070139527Ssam * once negotiated in which case we'll need to make this 1071139527Ssam * configurable. 1072139527Ssam */ 1073170530Ssam addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) && 1074178354Ssam (m->m_flags & M_EAPOL) == 0; 1075139527Ssam if (addqos) 1076138568Ssam hdrsize = sizeof(struct ieee80211_qosframe); 1077138568Ssam else 1078138568Ssam hdrsize = sizeof(struct ieee80211_frame); 1079195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 1080195618Srpaulo if (vap->iv_opmode == IEEE80211_M_MBSS) { 1081195618Srpaulo /* 1082195618Srpaulo * Mesh data frames are encapsulated according to the 1083195618Srpaulo * rules of Section 11B.8.5 (p.139 of D3.0 spec). 1084195618Srpaulo * o Group Addressed data (aka multicast) originating 1085195618Srpaulo * at the local sta are sent w/ 3-address format and 1086195618Srpaulo * address extension mode 00 1087195618Srpaulo * o Individually Addressed data (aka unicast) originating 1088195618Srpaulo * at the local sta are sent w/ 4-address format and 1089195618Srpaulo * address extension mode 00 1090195618Srpaulo * o Group Addressed data forwarded from a non-mesh sta are 1091195618Srpaulo * sent w/ 3-address format and address extension mode 01 1092195618Srpaulo * o Individually Address data from another sta are sent 1093195618Srpaulo * w/ 4-address format and address extension mode 10 1094195618Srpaulo */ 1095195618Srpaulo is4addr = 0; /* NB: don't use, disable */ 1096195784Srpaulo if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) 1097195784Srpaulo hdrsize += IEEE80211_ADDR_LEN; /* unicast are 4-addr */ 1098195618Srpaulo meshhdrsize = sizeof(struct ieee80211_meshcntl); 1099195618Srpaulo /* XXX defines for AE modes */ 1100195618Srpaulo if (IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { 1101195784Srpaulo if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) 1102195618Srpaulo meshae = 0; 1103195784Srpaulo else 1104195618Srpaulo meshae = 4; /* NB: pseudo */ 1105195618Srpaulo } else if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) { 1106195618Srpaulo meshae = 1; 1107195784Srpaulo meshhdrsize += 1*IEEE80211_ADDR_LEN; 1108195618Srpaulo } else { 1109195618Srpaulo meshae = 2; 1110195784Srpaulo meshhdrsize += 2*IEEE80211_ADDR_LEN; 1111195618Srpaulo } 1112195618Srpaulo } else { 1113195618Srpaulo#endif 1114195618Srpaulo /* 1115195618Srpaulo * 4-address frames need to be generated for: 1116195618Srpaulo * o packets sent through a WDS vap (IEEE80211_M_WDS) 1117195618Srpaulo * o packets sent through a vap marked for relaying 1118195618Srpaulo * (e.g. a station operating with dynamic WDS) 1119195618Srpaulo */ 1120195618Srpaulo is4addr = vap->iv_opmode == IEEE80211_M_WDS || 1121195618Srpaulo ((vap->iv_flags_ext & IEEE80211_FEXT_4ADDR) && 1122195618Srpaulo !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)); 1123195618Srpaulo if (is4addr) 1124195618Srpaulo hdrsize += IEEE80211_ADDR_LEN; 1125195618Srpaulo meshhdrsize = meshae = 0; 1126195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 1127195618Srpaulo } 1128195618Srpaulo#endif 1129178354Ssam /* 1130178354Ssam * Honor driver DATAPAD requirement. 1131178354Ssam */ 1132138568Ssam if (ic->ic_flags & IEEE80211_F_DATAPAD) 1133178354Ssam hdrspace = roundup(hdrsize, sizeof(uint32_t)); 1134178354Ssam else 1135178354Ssam hdrspace = hdrsize; 1136170530Ssam 1137190391Ssam if (__predict_true((m->m_flags & M_FF) == 0)) { 1138170530Ssam /* 1139170530Ssam * Normal frame. 1140170530Ssam */ 1141195618Srpaulo m = ieee80211_mbuf_adjust(vap, hdrspace + meshhdrsize, key, m); 1142170530Ssam if (m == NULL) { 1143170530Ssam /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ 1144170530Ssam goto bad; 1145170530Ssam } 1146170530Ssam /* NB: this could be optimized 'cuz of ieee80211_mbuf_adjust */ 1147170530Ssam m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 1148170530Ssam llc = mtod(m, struct llc *); 1149170530Ssam llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 1150170530Ssam llc->llc_control = LLC_UI; 1151170530Ssam llc->llc_snap.org_code[0] = 0; 1152170530Ssam llc->llc_snap.org_code[1] = 0; 1153170530Ssam llc->llc_snap.org_code[2] = 0; 1154170530Ssam llc->llc_snap.ether_type = eh.ether_type; 1155190391Ssam } else { 1156190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 1157190579Ssam /* 1158190579Ssam * Aggregated frame. 1159190579Ssam */ 1160195618Srpaulo m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); 1161190391Ssam if (m == NULL) 1162190391Ssam#endif 1163190391Ssam goto bad; 1164127772Ssam } 1165138568Ssam datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ 1166138568Ssam 1167195618Srpaulo M_PREPEND(m, hdrspace + meshhdrsize, M_DONTWAIT); 1168121180Ssam if (m == NULL) { 1169178354Ssam vap->iv_stats.is_tx_nobuf++; 1170119150Ssam goto bad; 1171121180Ssam } 1172116742Ssam wh = mtod(m, struct ieee80211_frame *); 1173116742Ssam wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 1174170530Ssam *(uint16_t *)wh->i_dur = 0; 1175195618Srpaulo qos = NULL; /* NB: quiet compiler */ 1176178354Ssam if (is4addr) { 1177178354Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 1178178354Ssam IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 1179178354Ssam IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 1180178354Ssam IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 1181178354Ssam IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); 1182178354Ssam } else switch (vap->iv_opmode) { 1183116742Ssam case IEEE80211_M_STA: 1184116742Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 1185116742Ssam IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); 1186116742Ssam IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 1187116742Ssam IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 1188116742Ssam break; 1189116742Ssam case IEEE80211_M_IBSS: 1190116742Ssam case IEEE80211_M_AHDEMO: 1191116742Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 1192116742Ssam IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1193116742Ssam IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 1194140636Ssam /* 1195178354Ssam * NB: always use the bssid from iv_bss as the 1196140636Ssam * neighbor's may be stale after an ibss merge 1197140636Ssam */ 1198178354Ssam IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid); 1199116742Ssam break; 1200116742Ssam case IEEE80211_M_HOSTAP: 1201116742Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 1202116742Ssam IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1203116742Ssam IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); 1204116742Ssam IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 1205116742Ssam break; 1206195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 1207195618Srpaulo case IEEE80211_M_MBSS: 1208195618Srpaulo /* NB: offset by hdrspace to deal with DATAPAD */ 1209195784Srpaulo mc = (struct ieee80211_meshcntl_ae10 *) 1210195618Srpaulo (mtod(m, uint8_t *) + hdrspace); 1211195618Srpaulo switch (meshae) { 1212195618Srpaulo case 0: /* ucast, no proxy */ 1213195618Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 1214195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 1215195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 1216195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 1217195618Srpaulo IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); 1218195618Srpaulo mc->mc_flags = 0; 1219195618Srpaulo qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; 1220195618Srpaulo break; 1221195618Srpaulo case 4: /* mcast, no proxy */ 1222195618Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 1223195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1224195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 1225195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 1226195618Srpaulo mc->mc_flags = 0; /* NB: AE is really 0 */ 1227195618Srpaulo qos = ((struct ieee80211_qosframe *) wh)->i_qos; 1228195618Srpaulo break; 1229195618Srpaulo case 1: /* mcast, proxy */ 1230195618Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 1231195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1232195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 1233195784Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_myaddr); 1234195618Srpaulo mc->mc_flags = 1; 1235195618Srpaulo IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_shost); 1236195618Srpaulo qos = ((struct ieee80211_qosframe *) wh)->i_qos; 1237195618Srpaulo break; 1238195618Srpaulo case 2: /* ucast, proxy */ 1239195618Srpaulo wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 1240195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 1241195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 1242195784Srpaulo /* XXX not right, need MeshDA */ 1243195618Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 1244195784Srpaulo /* XXX assume are MeshSA */ 1245195784Srpaulo IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, vap->iv_myaddr); 1246195618Srpaulo mc->mc_flags = 2; 1247195784Srpaulo IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_dhost); 1248195618Srpaulo IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_shost); 1249195618Srpaulo qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; 1250195618Srpaulo break; 1251195618Srpaulo default: 1252195618Srpaulo KASSERT(0, ("meshae %d", meshae)); 1253195618Srpaulo break; 1254195618Srpaulo } 1255195618Srpaulo mc->mc_ttl = ms->ms_ttl; 1256195618Srpaulo ms->ms_seq++; 1257195618Srpaulo LE_WRITE_4(mc->mc_seq, ms->ms_seq); 1258195618Srpaulo break; 1259195618Srpaulo#endif 1260178354Ssam case IEEE80211_M_WDS: /* NB: is4addr should always be true */ 1261195618Srpaulo default: 1262119150Ssam goto bad; 1263116742Ssam } 1264190678Ssam if (m->m_flags & M_MORE_DATA) 1265147789Ssam wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 1266139527Ssam if (addqos) { 1267138568Ssam int ac, tid; 1268138568Ssam 1269178354Ssam if (is4addr) { 1270178354Ssam qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; 1271195618Srpaulo /* NB: mesh case handled earlier */ 1272195618Srpaulo } else if (vap->iv_opmode != IEEE80211_M_MBSS) 1273178354Ssam qos = ((struct ieee80211_qosframe *) wh)->i_qos; 1274138568Ssam ac = M_WME_GETAC(m); 1275138568Ssam /* map from access class/queue to 11e header priorty value */ 1276138568Ssam tid = WME_AC_TO_TID(ac); 1277178354Ssam qos[0] = tid & IEEE80211_QOS_TID; 1278138568Ssam if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) 1279178354Ssam qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; 1280178354Ssam qos[1] = 0; 1281178354Ssam wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; 1282138568Ssam 1283183247Ssam if ((m->m_flags & M_AMPDU_MPDU) == 0) { 1284183247Ssam /* 1285183247Ssam * NB: don't assign a sequence # to potential 1286183247Ssam * aggregates; we expect this happens at the 1287183247Ssam * point the frame comes off any aggregation q 1288183247Ssam * as otherwise we may introduce holes in the 1289183247Ssam * BA sequence space and/or make window accouting 1290183247Ssam * more difficult. 1291183247Ssam * 1292183247Ssam * XXX may want to control this with a driver 1293183247Ssam * capability; this may also change when we pull 1294183247Ssam * aggregation up into net80211 1295183247Ssam */ 1296191544Ssam seqno = ni->ni_txseqs[tid]++; 1297183247Ssam *(uint16_t *)wh->i_seq = 1298191544Ssam htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 1299191571Ssam M_SEQNO_SET(m, seqno); 1300183247Ssam } 1301138568Ssam } else { 1302191544Ssam seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; 1303170530Ssam *(uint16_t *)wh->i_seq = 1304191544Ssam htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 1305191571Ssam M_SEQNO_SET(m, seqno); 1306138568Ssam } 1307191571Ssam 1308195618Srpaulo 1309170530Ssam /* check if xmit fragmentation is required */ 1310178354Ssam txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold && 1311170530Ssam !IEEE80211_IS_MULTICAST(wh->i_addr1) && 1312178354Ssam (vap->iv_caps & IEEE80211_C_TXFRAG) && 1313191545Ssam (m->m_flags & (M_FF | M_AMPDU_MPDU)) == 0); 1314139527Ssam if (key != NULL) { 1315139527Ssam /* 1316139527Ssam * IEEE 802.1X: send EAPOL frames always in the clear. 1317139527Ssam * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. 1318139527Ssam */ 1319178354Ssam if ((m->m_flags & M_EAPOL) == 0 || 1320178354Ssam ((vap->iv_flags & IEEE80211_F_WPA) && 1321178354Ssam (vap->iv_opmode == IEEE80211_M_STA ? 1322167432Ssam !IEEE80211_KEY_UNDEFINED(key) : 1323167432Ssam !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { 1324139527Ssam wh->i_fc[1] |= IEEE80211_FC1_WEP; 1325178354Ssam if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) { 1326178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, 1327178354Ssam eh.ether_dhost, 1328178354Ssam "%s", "enmic failed, discard frame"); 1329178354Ssam vap->iv_stats.is_crypto_enmicfail++; 1330139527Ssam goto bad; 1331139527Ssam } 1332139527Ssam } 1333139527Ssam } 1334178354Ssam if (txfrag && !ieee80211_fragment(vap, m, hdrsize, 1335178354Ssam key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold)) 1336170530Ssam goto bad; 1337138568Ssam 1338184286Ssam m->m_flags |= M_ENCAP; /* mark encapsulated */ 1339184286Ssam 1340138568Ssam IEEE80211_NODE_STAT(ni, tx_data); 1341191544Ssam if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1342161144Ssam IEEE80211_NODE_STAT(ni, tx_mcast); 1343191544Ssam m->m_flags |= M_MCAST; 1344191544Ssam } else 1345161144Ssam IEEE80211_NODE_STAT(ni, tx_ucast); 1346138568Ssam IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); 1347138568Ssam 1348116742Ssam return m; 1349119150Ssambad: 1350119150Ssam if (m != NULL) 1351119150Ssam m_freem(m); 1352119150Ssam return NULL; 1353178354Ssam#undef WH4 1354116742Ssam} 1355116742Ssam 1356116742Ssam/* 1357170530Ssam * Fragment the frame according to the specified mtu. 1358170530Ssam * The size of the 802.11 header (w/o padding) is provided 1359170530Ssam * so we don't need to recalculate it. We create a new 1360170530Ssam * mbuf for each fragment and chain it through m_nextpkt; 1361170530Ssam * we might be able to optimize this by reusing the original 1362170530Ssam * packet's mbufs but that is significantly more complicated. 1363170530Ssam */ 1364170530Ssamstatic int 1365178354Ssamieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, 1366170530Ssam u_int hdrsize, u_int ciphdrsize, u_int mtu) 1367170530Ssam{ 1368170530Ssam struct ieee80211_frame *wh, *whf; 1369170530Ssam struct mbuf *m, *prev, *next; 1370170530Ssam u_int totalhdrsize, fragno, fragsize, off, remainder, payload; 1371170530Ssam 1372170530Ssam KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?")); 1373170530Ssam KASSERT(m0->m_pkthdr.len > mtu, 1374170530Ssam ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu)); 1375170530Ssam 1376170530Ssam wh = mtod(m0, struct ieee80211_frame *); 1377170530Ssam /* NB: mark the first frag; it will be propagated below */ 1378170530Ssam wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG; 1379170530Ssam totalhdrsize = hdrsize + ciphdrsize; 1380170530Ssam fragno = 1; 1381170530Ssam off = mtu - ciphdrsize; 1382170530Ssam remainder = m0->m_pkthdr.len - off; 1383170530Ssam prev = m0; 1384170530Ssam do { 1385170530Ssam fragsize = totalhdrsize + remainder; 1386170530Ssam if (fragsize > mtu) 1387170530Ssam fragsize = mtu; 1388178354Ssam /* XXX fragsize can be >2048! */ 1389170530Ssam KASSERT(fragsize < MCLBYTES, 1390170530Ssam ("fragment size %u too big!", fragsize)); 1391170530Ssam if (fragsize > MHLEN) 1392170530Ssam m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1393170530Ssam else 1394170530Ssam m = m_gethdr(M_DONTWAIT, MT_DATA); 1395170530Ssam if (m == NULL) 1396170530Ssam goto bad; 1397170530Ssam /* leave room to prepend any cipher header */ 1398170530Ssam m_align(m, fragsize - ciphdrsize); 1399170530Ssam 1400170530Ssam /* 1401170530Ssam * Form the header in the fragment. Note that since 1402170530Ssam * we mark the first fragment with the MORE_FRAG bit 1403170530Ssam * it automatically is propagated to each fragment; we 1404170530Ssam * need only clear it on the last fragment (done below). 1405170530Ssam */ 1406170530Ssam whf = mtod(m, struct ieee80211_frame *); 1407170530Ssam memcpy(whf, wh, hdrsize); 1408170530Ssam *(uint16_t *)&whf->i_seq[0] |= htole16( 1409170530Ssam (fragno & IEEE80211_SEQ_FRAG_MASK) << 1410170530Ssam IEEE80211_SEQ_FRAG_SHIFT); 1411170530Ssam fragno++; 1412170530Ssam 1413170530Ssam payload = fragsize - totalhdrsize; 1414170530Ssam /* NB: destination is known to be contiguous */ 1415170530Ssam m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize); 1416170530Ssam m->m_len = hdrsize + payload; 1417170530Ssam m->m_pkthdr.len = hdrsize + payload; 1418170530Ssam m->m_flags |= M_FRAG; 1419170530Ssam 1420170530Ssam /* chain up the fragment */ 1421170530Ssam prev->m_nextpkt = m; 1422170530Ssam prev = m; 1423170530Ssam 1424170530Ssam /* deduct fragment just formed */ 1425170530Ssam remainder -= payload; 1426170530Ssam off += payload; 1427170530Ssam } while (remainder != 0); 1428188380Sweongyo 1429188380Sweongyo /* set the last fragment */ 1430188380Sweongyo m->m_flags |= M_LASTFRAG; 1431170530Ssam whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG; 1432170530Ssam 1433170530Ssam /* strip first mbuf now that everything has been copied */ 1434170530Ssam m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); 1435170530Ssam m0->m_flags |= M_FIRSTFRAG | M_FRAG; 1436170530Ssam 1437178354Ssam vap->iv_stats.is_tx_fragframes++; 1438178354Ssam vap->iv_stats.is_tx_frags += fragno-1; 1439170530Ssam 1440170530Ssam return 1; 1441170530Ssambad: 1442170530Ssam /* reclaim fragments but leave original frame for caller to free */ 1443170530Ssam for (m = m0->m_nextpkt; m != NULL; m = next) { 1444170530Ssam next = m->m_nextpkt; 1445170530Ssam m->m_nextpkt = NULL; /* XXX paranoid */ 1446170530Ssam m_freem(m); 1447170530Ssam } 1448170530Ssam m0->m_nextpkt = NULL; 1449170530Ssam return 0; 1450170530Ssam} 1451170530Ssam 1452170530Ssam/* 1453116742Ssam * Add a supported rates element id to a frame. 1454116742Ssam */ 1455195618Srpaulouint8_t * 1456170530Ssamieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) 1457116742Ssam{ 1458116742Ssam int nrates; 1459116742Ssam 1460116742Ssam *frm++ = IEEE80211_ELEMID_RATES; 1461116742Ssam nrates = rs->rs_nrates; 1462116742Ssam if (nrates > IEEE80211_RATE_SIZE) 1463116742Ssam nrates = IEEE80211_RATE_SIZE; 1464116742Ssam *frm++ = nrates; 1465116742Ssam memcpy(frm, rs->rs_rates, nrates); 1466116742Ssam return frm + nrates; 1467116742Ssam} 1468116742Ssam 1469116742Ssam/* 1470116742Ssam * Add an extended supported rates element id to a frame. 1471116742Ssam */ 1472195618Srpaulouint8_t * 1473170530Ssamieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) 1474116742Ssam{ 1475116742Ssam /* 1476116742Ssam * Add an extended supported rates element if operating in 11g mode. 1477116742Ssam */ 1478116742Ssam if (rs->rs_nrates > IEEE80211_RATE_SIZE) { 1479116742Ssam int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 1480116742Ssam *frm++ = IEEE80211_ELEMID_XRATES; 1481116742Ssam *frm++ = nrates; 1482116742Ssam memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 1483116742Ssam frm += nrates; 1484116742Ssam } 1485116742Ssam return frm; 1486116742Ssam} 1487116742Ssam 1488116742Ssam/* 1489178354Ssam * Add an ssid element to a frame. 1490116742Ssam */ 1491170530Ssamstatic uint8_t * 1492170530Ssamieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) 1493116742Ssam{ 1494116742Ssam *frm++ = IEEE80211_ELEMID_SSID; 1495116742Ssam *frm++ = len; 1496116742Ssam memcpy(frm, ssid, len); 1497116742Ssam return frm + len; 1498116742Ssam} 1499116742Ssam 1500138568Ssam/* 1501138568Ssam * Add an erp element to a frame. 1502138568Ssam */ 1503170530Ssamstatic uint8_t * 1504170530Ssamieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) 1505116742Ssam{ 1506170530Ssam uint8_t erp; 1507116742Ssam 1508138568Ssam *frm++ = IEEE80211_ELEMID_ERP; 1509138568Ssam *frm++ = 1; 1510138568Ssam erp = 0; 1511138568Ssam if (ic->ic_nonerpsta != 0) 1512138568Ssam erp |= IEEE80211_ERP_NON_ERP_PRESENT; 1513138568Ssam if (ic->ic_flags & IEEE80211_F_USEPROT) 1514138568Ssam erp |= IEEE80211_ERP_USE_PROTECTION; 1515138568Ssam if (ic->ic_flags & IEEE80211_F_USEBARKER) 1516138568Ssam erp |= IEEE80211_ERP_LONG_PREAMBLE; 1517138568Ssam *frm++ = erp; 1518138568Ssam return frm; 1519138568Ssam} 1520138568Ssam 1521178354Ssam/* 1522178354Ssam * Add a CFParams element to a frame. 1523178354Ssam */ 1524170530Ssamstatic uint8_t * 1525178354Ssamieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic) 1526138568Ssam{ 1527195618Srpaulo#define ADDSHORT(frm, v) do { \ 1528195618Srpaulo LE_WRITE_2(frm, v); \ 1529195618Srpaulo frm += 2; \ 1530138568Ssam} while (0) 1531178354Ssam *frm++ = IEEE80211_ELEMID_CFPARMS; 1532178354Ssam *frm++ = 6; 1533178354Ssam *frm++ = 0; /* CFP count */ 1534178354Ssam *frm++ = 2; /* CFP period */ 1535178354Ssam ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */ 1536178354Ssam ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */ 1537138568Ssam return frm; 1538138568Ssam#undef ADDSHORT 1539116742Ssam} 1540116742Ssam 1541178354Ssamstatic __inline uint8_t * 1542178354Ssamadd_appie(uint8_t *frm, const struct ieee80211_appie *ie) 1543138568Ssam{ 1544178354Ssam memcpy(frm, ie->ie_data, ie->ie_len); 1545178354Ssam return frm + ie->ie_len; 1546138568Ssam} 1547138568Ssam 1548178354Ssamstatic __inline uint8_t * 1549178354Ssamadd_ie(uint8_t *frm, const uint8_t *ie) 1550138568Ssam{ 1551178354Ssam memcpy(frm, ie, 2 + ie[1]); 1552178354Ssam return frm + 2 + ie[1]; 1553138568Ssam} 1554138568Ssam 1555138568Ssam#define WME_OUI_BYTES 0x00, 0x50, 0xf2 1556138568Ssam/* 1557138568Ssam * Add a WME information element to a frame. 1558138568Ssam */ 1559170530Ssamstatic uint8_t * 1560170530Ssamieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) 1561138568Ssam{ 1562138568Ssam static const struct ieee80211_wme_info info = { 1563138568Ssam .wme_id = IEEE80211_ELEMID_VENDOR, 1564138568Ssam .wme_len = sizeof(struct ieee80211_wme_info) - 2, 1565138568Ssam .wme_oui = { WME_OUI_BYTES }, 1566138568Ssam .wme_type = WME_OUI_TYPE, 1567138568Ssam .wme_subtype = WME_INFO_OUI_SUBTYPE, 1568138568Ssam .wme_version = WME_VERSION, 1569138568Ssam .wme_info = 0, 1570138568Ssam }; 1571138568Ssam memcpy(frm, &info, sizeof(info)); 1572138568Ssam return frm + sizeof(info); 1573138568Ssam} 1574138568Ssam 1575138568Ssam/* 1576138568Ssam * Add a WME parameters element to a frame. 1577138568Ssam */ 1578170530Ssamstatic uint8_t * 1579170530Ssamieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) 1580138568Ssam{ 1581138568Ssam#define SM(_v, _f) (((_v) << _f##_S) & _f) 1582195618Srpaulo#define ADDSHORT(frm, v) do { \ 1583195618Srpaulo LE_WRITE_2(frm, v); \ 1584195618Srpaulo frm += 2; \ 1585138568Ssam} while (0) 1586138568Ssam /* NB: this works 'cuz a param has an info at the front */ 1587138568Ssam static const struct ieee80211_wme_info param = { 1588138568Ssam .wme_id = IEEE80211_ELEMID_VENDOR, 1589138568Ssam .wme_len = sizeof(struct ieee80211_wme_param) - 2, 1590138568Ssam .wme_oui = { WME_OUI_BYTES }, 1591138568Ssam .wme_type = WME_OUI_TYPE, 1592138568Ssam .wme_subtype = WME_PARAM_OUI_SUBTYPE, 1593138568Ssam .wme_version = WME_VERSION, 1594138568Ssam }; 1595138568Ssam int i; 1596138568Ssam 1597138568Ssam memcpy(frm, ¶m, sizeof(param)); 1598138568Ssam frm += __offsetof(struct ieee80211_wme_info, wme_info); 1599138568Ssam *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ 1600138568Ssam *frm++ = 0; /* reserved field */ 1601138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 1602138568Ssam const struct wmeParams *ac = 1603138568Ssam &wme->wme_bssChanParams.cap_wmeParams[i]; 1604138568Ssam *frm++ = SM(i, WME_PARAM_ACI) 1605138568Ssam | SM(ac->wmep_acm, WME_PARAM_ACM) 1606138568Ssam | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) 1607138568Ssam ; 1608138568Ssam *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) 1609138568Ssam | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) 1610138568Ssam ; 1611138568Ssam ADDSHORT(frm, ac->wmep_txopLimit); 1612138568Ssam } 1613138568Ssam return frm; 1614138568Ssam#undef SM 1615138568Ssam#undef ADDSHORT 1616138568Ssam} 1617138568Ssam#undef WME_OUI_BYTES 1618138568Ssam 1619138568Ssam/* 1620178354Ssam * Add an 11h Power Constraint element to a frame. 1621178354Ssam */ 1622178354Ssamstatic uint8_t * 1623178354Ssamieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap) 1624178354Ssam{ 1625178354Ssam const struct ieee80211_channel *c = vap->iv_bss->ni_chan; 1626178354Ssam /* XXX per-vap tx power limit? */ 1627178354Ssam int8_t limit = vap->iv_ic->ic_txpowlimit / 2; 1628178354Ssam 1629178354Ssam frm[0] = IEEE80211_ELEMID_PWRCNSTR; 1630178354Ssam frm[1] = 1; 1631178354Ssam frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0; 1632178354Ssam return frm + 3; 1633178354Ssam} 1634178354Ssam 1635178354Ssam/* 1636178354Ssam * Add an 11h Power Capability element to a frame. 1637178354Ssam */ 1638178354Ssamstatic uint8_t * 1639178354Ssamieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c) 1640178354Ssam{ 1641178354Ssam frm[0] = IEEE80211_ELEMID_PWRCAP; 1642178354Ssam frm[1] = 2; 1643178354Ssam frm[2] = c->ic_minpower; 1644178354Ssam frm[3] = c->ic_maxpower; 1645178354Ssam return frm + 4; 1646178354Ssam} 1647178354Ssam 1648178354Ssam/* 1649178354Ssam * Add an 11h Supported Channels element to a frame. 1650178354Ssam */ 1651178354Ssamstatic uint8_t * 1652178354Ssamieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic) 1653178354Ssam{ 1654178354Ssam static const int ielen = 26; 1655178354Ssam 1656178354Ssam frm[0] = IEEE80211_ELEMID_SUPPCHAN; 1657178354Ssam frm[1] = ielen; 1658178354Ssam /* XXX not correct */ 1659178354Ssam memcpy(frm+2, ic->ic_chan_avail, ielen); 1660178354Ssam return frm + 2 + ielen; 1661178354Ssam} 1662178354Ssam 1663178354Ssam/* 1664178354Ssam * Add an 11h Channel Switch Announcement element to a frame. 1665178354Ssam * Note that we use the per-vap CSA count to adjust the global 1666178354Ssam * counter so we can use this routine to form probe response 1667178354Ssam * frames and get the current count. 1668178354Ssam */ 1669178354Ssamstatic uint8_t * 1670178354Ssamieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap) 1671178354Ssam{ 1672178354Ssam struct ieee80211com *ic = vap->iv_ic; 1673178354Ssam struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm; 1674178354Ssam 1675193439Ssam csa->csa_ie = IEEE80211_ELEMID_CSA; 1676178354Ssam csa->csa_len = 3; 1677178354Ssam csa->csa_mode = 1; /* XXX force quiet on channel */ 1678178354Ssam csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan); 1679178354Ssam csa->csa_count = ic->ic_csa_count - vap->iv_csa_count; 1680178354Ssam return frm + sizeof(*csa); 1681178354Ssam} 1682178354Ssam 1683178354Ssam/* 1684178354Ssam * Add an 11h country information element to a frame. 1685178354Ssam */ 1686178354Ssamstatic uint8_t * 1687178354Ssamieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) 1688178354Ssam{ 1689178354Ssam 1690178354Ssam if (ic->ic_countryie == NULL || 1691178354Ssam ic->ic_countryie_chan != ic->ic_bsschan) { 1692178354Ssam /* 1693178354Ssam * Handle lazy construction of ie. This is done on 1694178354Ssam * first use and after a channel change that requires 1695178354Ssam * re-calculation. 1696178354Ssam */ 1697178354Ssam if (ic->ic_countryie != NULL) 1698178354Ssam free(ic->ic_countryie, M_80211_NODE_IE); 1699178354Ssam ic->ic_countryie = ieee80211_alloc_countryie(ic); 1700178354Ssam if (ic->ic_countryie == NULL) 1701178354Ssam return frm; 1702178354Ssam ic->ic_countryie_chan = ic->ic_bsschan; 1703178354Ssam } 1704178354Ssam return add_appie(frm, ic->ic_countryie); 1705178354Ssam} 1706178354Ssam 1707266507Shselaskyuint8_t * 1708266507Shselaskyieee80211_add_wpa(uint8_t *frm, const struct ieee80211vap *vap) 1709266507Shselasky{ 1710266507Shselasky if (vap->iv_flags & IEEE80211_F_WPA1 && vap->iv_wpa_ie != NULL) 1711266507Shselasky return (add_ie(frm, vap->iv_wpa_ie)); 1712266507Shselasky else { 1713266507Shselasky /* XXX else complain? */ 1714266507Shselasky return (frm); 1715266507Shselasky } 1716266507Shselasky} 1717266507Shselasky 1718266507Shselaskyuint8_t * 1719266507Shselaskyieee80211_add_rsn(uint8_t *frm, const struct ieee80211vap *vap) 1720266507Shselasky{ 1721266507Shselasky if (vap->iv_flags & IEEE80211_F_WPA2 && vap->iv_rsn_ie != NULL) 1722266507Shselasky return (add_ie(frm, vap->iv_rsn_ie)); 1723266507Shselasky else { 1724266507Shselasky /* XXX else complain? */ 1725266507Shselasky return (frm); 1726266507Shselasky } 1727266507Shselasky} 1728266507Shselasky 1729266507Shselaskyuint8_t * 1730266507Shselaskyieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni) 1731266507Shselasky{ 1732266507Shselasky if (ni->ni_flags & IEEE80211_NODE_QOS) { 1733266507Shselasky *frm++ = IEEE80211_ELEMID_QOS; 1734266507Shselasky *frm++ = 1; 1735266507Shselasky *frm++ = 0; 1736266507Shselasky } 1737266507Shselasky 1738266507Shselasky return (frm); 1739266507Shselasky} 1740266507Shselasky 1741178354Ssam/* 1742148315Ssam * Send a probe request frame with the specified ssid 1743148315Ssam * and any optional information element data. 1744148315Ssam */ 1745148315Ssamint 1746148315Ssamieee80211_send_probereq(struct ieee80211_node *ni, 1747170530Ssam const uint8_t sa[IEEE80211_ADDR_LEN], 1748170530Ssam const uint8_t da[IEEE80211_ADDR_LEN], 1749170530Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 1750178354Ssam const uint8_t *ssid, size_t ssidlen) 1751148315Ssam{ 1752178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1753148315Ssam struct ieee80211com *ic = ni->ni_ic; 1754184284Ssam const struct ieee80211_txparam *tp; 1755184284Ssam struct ieee80211_bpf_params params; 1756148315Ssam struct ieee80211_frame *wh; 1757165569Ssam const struct ieee80211_rateset *rs; 1758148315Ssam struct mbuf *m; 1759170530Ssam uint8_t *frm; 1760148315Ssam 1761178354Ssam if (vap->iv_state == IEEE80211_S_CAC) { 1762178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, 1763178354Ssam "block %s frame in CAC state", "probe request"); 1764178354Ssam vap->iv_stats.is_tx_badstate++; 1765178354Ssam return EIO; /* XXX */ 1766178354Ssam } 1767178354Ssam 1768148315Ssam /* 1769148315Ssam * Hold a reference on the node so it doesn't go away until after 1770148315Ssam * the xmit is complete all the way in the driver. On error we 1771148315Ssam * will remove our reference. 1772148315Ssam */ 1773178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 1774148315Ssam "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 1775148315Ssam __func__, __LINE__, 1776148315Ssam ni, ether_sprintf(ni->ni_macaddr), 1777148315Ssam ieee80211_node_refcnt(ni)+1); 1778148315Ssam ieee80211_ref_node(ni); 1779148315Ssam 1780148315Ssam /* 1781148315Ssam * prreq frame format 1782148315Ssam * [tlv] ssid 1783148315Ssam * [tlv] supported rates 1784178354Ssam * [tlv] RSN (optional) 1785148315Ssam * [tlv] extended supported rates 1786178354Ssam * [tlv] WPA (optional) 1787148315Ssam * [tlv] user-specified ie's 1788148315Ssam */ 1789148315Ssam m = ieee80211_getmgtframe(&frm, 1790170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1791178354Ssam 2 + IEEE80211_NWID_LEN 1792148315Ssam + 2 + IEEE80211_RATE_SIZE 1793178354Ssam + sizeof(struct ieee80211_ie_wpa) 1794148315Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 1795178354Ssam + sizeof(struct ieee80211_ie_wpa) 1796178354Ssam + (vap->iv_appie_probereq != NULL ? 1797178354Ssam vap->iv_appie_probereq->ie_len : 0) 1798148315Ssam ); 1799148315Ssam if (m == NULL) { 1800178354Ssam vap->iv_stats.is_tx_nobuf++; 1801148315Ssam ieee80211_free_node(ni); 1802148315Ssam return ENOMEM; 1803148315Ssam } 1804148315Ssam 1805148315Ssam frm = ieee80211_add_ssid(frm, ssid, ssidlen); 1806165569Ssam rs = ieee80211_get_suprates(ic, ic->ic_curchan); 1807165569Ssam frm = ieee80211_add_rates(frm, rs); 1808266507Shselasky frm = ieee80211_add_rsn(frm, vap); 1809165569Ssam frm = ieee80211_add_xrates(frm, rs); 1810266507Shselasky frm = ieee80211_add_wpa(frm, vap); 1811178354Ssam if (vap->iv_appie_probereq != NULL) 1812178354Ssam frm = add_appie(frm, vap->iv_appie_probereq); 1813170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1814148315Ssam 1815184284Ssam KASSERT(M_LEADINGSPACE(m) >= sizeof(struct ieee80211_frame), 1816184284Ssam ("leading space %zd", M_LEADINGSPACE(m))); 1817148315Ssam M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 1818184284Ssam if (m == NULL) { 1819184284Ssam /* NB: cannot happen */ 1820184284Ssam ieee80211_free_node(ni); 1821148315Ssam return ENOMEM; 1822184284Ssam } 1823148315Ssam 1824148315Ssam wh = mtod(m, struct ieee80211_frame *); 1825191544Ssam ieee80211_send_setup(ni, m, 1826184282Ssam IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, 1827184282Ssam IEEE80211_NONQOS_TID, sa, da, bssid); 1828148315Ssam /* XXX power management? */ 1829184286Ssam m->m_flags |= M_ENCAP; /* mark encapsulated */ 1830148315Ssam 1831178354Ssam M_WME_SETAC(m, WME_AC_BE); 1832178354Ssam 1833148315Ssam IEEE80211_NODE_STAT(ni, tx_probereq); 1834148315Ssam IEEE80211_NODE_STAT(ni, tx_mgmt); 1835148315Ssam 1836178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 1837178354Ssam "send probe req on channel %u bssid %s ssid \"%.*s\"\n", 1838178354Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), 1839178354Ssam ssidlen, ssid); 1840148315Ssam 1841184284Ssam memset(¶ms, 0, sizeof(params)); 1842184284Ssam params.ibp_pri = M_WME_GETAC(m); 1843184284Ssam tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; 1844184284Ssam params.ibp_rate0 = tp->mgmtrate; 1845184284Ssam if (IEEE80211_IS_MULTICAST(da)) { 1846184284Ssam params.ibp_flags |= IEEE80211_BPF_NOACK; 1847184284Ssam params.ibp_try0 = 1; 1848184284Ssam } else 1849184284Ssam params.ibp_try0 = tp->maxretry; 1850184284Ssam params.ibp_power = ni->ni_txpower; 1851184284Ssam return ic->ic_raw_xmit(ni, m, ¶ms); 1852148315Ssam} 1853148315Ssam 1854148315Ssam/* 1855155999Ssam * Calculate capability information for mgt frames. 1856155999Ssam */ 1857195618Srpaulouint16_t 1858195618Srpauloieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan) 1859155999Ssam{ 1860178354Ssam struct ieee80211com *ic = vap->iv_ic; 1861170530Ssam uint16_t capinfo; 1862155999Ssam 1863178354Ssam KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode")); 1864155999Ssam 1865178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP) 1866155999Ssam capinfo = IEEE80211_CAPINFO_ESS; 1867178354Ssam else if (vap->iv_opmode == IEEE80211_M_IBSS) 1868155999Ssam capinfo = IEEE80211_CAPINFO_IBSS; 1869155999Ssam else 1870155999Ssam capinfo = 0; 1871178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 1872155999Ssam capinfo |= IEEE80211_CAPINFO_PRIVACY; 1873155999Ssam if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 1874155999Ssam IEEE80211_IS_CHAN_2GHZ(chan)) 1875155999Ssam capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 1876155999Ssam if (ic->ic_flags & IEEE80211_F_SHSLOT) 1877155999Ssam capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 1878178354Ssam if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH)) 1879178354Ssam capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; 1880155999Ssam return capinfo; 1881155999Ssam} 1882155999Ssam 1883155999Ssam/* 1884119150Ssam * Send a management frame. The node is for the destination (or ic_bss 1885119150Ssam * when in station mode). Nodes other than ic_bss have their reference 1886119150Ssam * count bumped to reflect our use for an indeterminant time. 1887119150Ssam */ 1888116742Ssamint 1889178354Ssamieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) 1890116742Ssam{ 1891173273Ssam#define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT) 1892178354Ssam#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) 1893178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1894178354Ssam struct ieee80211com *ic = ni->ni_ic; 1895178354Ssam struct ieee80211_node *bss = vap->iv_bss; 1896184282Ssam struct ieee80211_bpf_params params; 1897116742Ssam struct mbuf *m; 1898170530Ssam uint8_t *frm; 1899170530Ssam uint16_t capinfo; 1900170530Ssam int has_challenge, is_shared_key, ret, status; 1901116742Ssam 1902119150Ssam KASSERT(ni != NULL, ("null node")); 1903119150Ssam 1904119150Ssam /* 1905119150Ssam * Hold a reference on the node so it doesn't go away until after 1906119150Ssam * the xmit is complete all the way in the driver. On error we 1907119150Ssam * will remove our reference. 1908119150Ssam */ 1909178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 1910140766Ssam "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 1911138568Ssam __func__, __LINE__, 1912140766Ssam ni, ether_sprintf(ni->ni_macaddr), 1913140766Ssam ieee80211_node_refcnt(ni)+1); 1914138568Ssam ieee80211_ref_node(ni); 1915138568Ssam 1916184282Ssam memset(¶ms, 0, sizeof(params)); 1917116742Ssam switch (type) { 1918116742Ssam 1919116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1920138568Ssam status = arg >> 16; 1921138568Ssam arg &= 0xffff; 1922138568Ssam has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || 1923138568Ssam arg == IEEE80211_AUTH_SHARED_RESPONSE) && 1924138568Ssam ni->ni_challenge != NULL); 1925138568Ssam 1926138568Ssam /* 1927138568Ssam * Deduce whether we're doing open authentication or 1928138568Ssam * shared key authentication. We do the latter if 1929138568Ssam * we're in the middle of a shared key authentication 1930138568Ssam * handshake or if we're initiating an authentication 1931138568Ssam * request and configured to use shared key. 1932138568Ssam */ 1933138568Ssam is_shared_key = has_challenge || 1934138568Ssam arg >= IEEE80211_AUTH_SHARED_RESPONSE || 1935138568Ssam (arg == IEEE80211_AUTH_SHARED_REQUEST && 1936178354Ssam bss->ni_authmode == IEEE80211_AUTH_SHARED); 1937138568Ssam 1938138568Ssam m = ieee80211_getmgtframe(&frm, 1939184282Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1940170530Ssam 3 * sizeof(uint16_t) 1941138568Ssam + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? 1942170530Ssam sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) 1943138568Ssam ); 1944116742Ssam if (m == NULL) 1945138568Ssam senderr(ENOMEM, is_tx_nobuf); 1946138568Ssam 1947170530Ssam ((uint16_t *)frm)[0] = 1948138568Ssam (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) 1949138568Ssam : htole16(IEEE80211_AUTH_ALG_OPEN); 1950170530Ssam ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */ 1951170530Ssam ((uint16_t *)frm)[2] = htole16(status);/* status */ 1952138568Ssam 1953138568Ssam if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { 1954170530Ssam ((uint16_t *)frm)[3] = 1955138568Ssam htole16((IEEE80211_CHALLENGE_LEN << 8) | 1956138568Ssam IEEE80211_ELEMID_CHALLENGE); 1957170530Ssam memcpy(&((uint16_t *)frm)[4], ni->ni_challenge, 1958138568Ssam IEEE80211_CHALLENGE_LEN); 1959138568Ssam m->m_pkthdr.len = m->m_len = 1960170530Ssam 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; 1961138568Ssam if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { 1962178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 1963178354Ssam "request encrypt frame (%s)", __func__); 1964184282Ssam /* mark frame for encryption */ 1965184282Ssam params.ibp_flags |= IEEE80211_BPF_CRYPTO; 1966138568Ssam } 1967138568Ssam } else 1968170530Ssam m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t); 1969138568Ssam 1970138568Ssam /* XXX not right for shared key */ 1971138568Ssam if (status == IEEE80211_STATUS_SUCCESS) 1972138568Ssam IEEE80211_NODE_STAT(ni, tx_auth); 1973138568Ssam else 1974138568Ssam IEEE80211_NODE_STAT(ni, tx_auth_fail); 1975138568Ssam 1976178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 1977170530Ssam ieee80211_add_callback(m, ieee80211_tx_mgt_cb, 1978178354Ssam (void *) vap->iv_state); 1979116742Ssam break; 1980116742Ssam 1981116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1982178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 1983178354Ssam "send station deauthenticate (reason %d)", arg); 1984170530Ssam m = ieee80211_getmgtframe(&frm, 1985170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1986170530Ssam sizeof(uint16_t)); 1987116742Ssam if (m == NULL) 1988138568Ssam senderr(ENOMEM, is_tx_nobuf); 1989170530Ssam *(uint16_t *)frm = htole16(arg); /* reason */ 1990170530Ssam m->m_pkthdr.len = m->m_len = sizeof(uint16_t); 1991138568Ssam 1992138568Ssam IEEE80211_NODE_STAT(ni, tx_deauth); 1993138568Ssam IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); 1994138568Ssam 1995148302Ssam ieee80211_node_unauthorize(ni); /* port closed */ 1996116742Ssam break; 1997116742Ssam 1998116742Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 1999116742Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 2000116742Ssam /* 2001116742Ssam * asreq frame format 2002116742Ssam * [2] capability information 2003116742Ssam * [2] listen interval 2004116742Ssam * [6*] current AP address (reassoc only) 2005116742Ssam * [tlv] ssid 2006116742Ssam * [tlv] supported rates 2007116742Ssam * [tlv] extended supported rates 2008178354Ssam * [4] power capability (optional) 2009178354Ssam * [28] supported channels (optional) 2010170530Ssam * [tlv] HT capabilities 2011178354Ssam * [tlv] WME (optional) 2012170530Ssam * [tlv] Vendor OUI HT capabilities (optional) 2013170530Ssam * [tlv] Atheros capabilities (if negotiated) 2014178354Ssam * [tlv] AppIE's (optional) 2015116742Ssam */ 2016138568Ssam m = ieee80211_getmgtframe(&frm, 2017170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 2018170530Ssam sizeof(uint16_t) 2019170530Ssam + sizeof(uint16_t) 2020116742Ssam + IEEE80211_ADDR_LEN 2021138568Ssam + 2 + IEEE80211_NWID_LEN 2022116742Ssam + 2 + IEEE80211_RATE_SIZE 2023138568Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 2024178354Ssam + 4 2025178354Ssam + 2 + 26 2026138568Ssam + sizeof(struct ieee80211_wme_info) 2027178354Ssam + sizeof(struct ieee80211_ie_htcap) 2028178354Ssam + 4 + sizeof(struct ieee80211_ie_htcap) 2029190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2030170530Ssam + sizeof(struct ieee80211_ath_ie) 2031190391Ssam#endif 2032178354Ssam + (vap->iv_appie_wpa != NULL ? 2033178354Ssam vap->iv_appie_wpa->ie_len : 0) 2034178354Ssam + (vap->iv_appie_assocreq != NULL ? 2035178354Ssam vap->iv_appie_assocreq->ie_len : 0) 2036138568Ssam ); 2037116742Ssam if (m == NULL) 2038138568Ssam senderr(ENOMEM, is_tx_nobuf); 2039116742Ssam 2040178354Ssam KASSERT(vap->iv_opmode == IEEE80211_M_STA, 2041178354Ssam ("wrong mode %u", vap->iv_opmode)); 2042155999Ssam capinfo = IEEE80211_CAPINFO_ESS; 2043178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 2044116742Ssam capinfo |= IEEE80211_CAPINFO_PRIVACY; 2045120070Ssam /* 2046120070Ssam * NB: Some 11a AP's reject the request when 2047120070Ssam * short premable is set. 2048120070Ssam */ 2049120070Ssam if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 2050148936Ssam IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 2051116742Ssam capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 2052170530Ssam if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 2053138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT)) 2054116742Ssam capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 2055173273Ssam if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) && 2056178354Ssam (vap->iv_flags & IEEE80211_F_DOTH)) 2057173273Ssam capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; 2058170530Ssam *(uint16_t *)frm = htole16(capinfo); 2059116742Ssam frm += 2; 2060116742Ssam 2061178354Ssam KASSERT(bss->ni_intval != 0, ("beacon interval is zero!")); 2062170530Ssam *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, 2063178354Ssam bss->ni_intval)); 2064116742Ssam frm += 2; 2065116742Ssam 2066116742Ssam if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 2067178354Ssam IEEE80211_ADDR_COPY(frm, bss->ni_bssid); 2068116742Ssam frm += IEEE80211_ADDR_LEN; 2069116742Ssam } 2070116742Ssam 2071116742Ssam frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 2072116742Ssam frm = ieee80211_add_rates(frm, &ni->ni_rates); 2073266507Shselasky frm = ieee80211_add_rsn(frm, vap); 2074116742Ssam frm = ieee80211_add_xrates(frm, &ni->ni_rates); 2075178354Ssam if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { 2076178354Ssam frm = ieee80211_add_powercapability(frm, 2077178354Ssam ic->ic_curchan); 2078178354Ssam frm = ieee80211_add_supportedchannels(frm, ic); 2079178354Ssam } 2080193655Ssam if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && 2081178354Ssam ni->ni_ies.htcap_ie != NULL && 2082178354Ssam ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) 2083173273Ssam frm = ieee80211_add_htcap(frm, ni); 2084266507Shselasky frm = ieee80211_add_wpa(frm, vap); 2085178354Ssam if ((ic->ic_flags & IEEE80211_F_WME) && 2086178354Ssam ni->ni_ies.wme_ie != NULL) 2087138568Ssam frm = ieee80211_add_wme_info(frm, &ic->ic_wme); 2088193655Ssam if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && 2089178354Ssam ni->ni_ies.htcap_ie != NULL && 2090178354Ssam ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) 2091173273Ssam frm = ieee80211_add_htcap_vendor(frm, ni); 2092190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2093190451Ssam if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) { 2094190451Ssam frm = ieee80211_add_ath(frm, 2095178354Ssam IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), 2096190451Ssam ((vap->iv_flags & IEEE80211_F_WPA) == 0 && 2097190451Ssam ni->ni_authmode != IEEE80211_AUTH_8021X) ? 2098190451Ssam vap->iv_def_txkey : IEEE80211_KEYIX_NONE); 2099190451Ssam } 2100190391Ssam#endif /* IEEE80211_SUPPORT_SUPERG */ 2101178354Ssam if (vap->iv_appie_assocreq != NULL) 2102178354Ssam frm = add_appie(frm, vap->iv_appie_assocreq); 2103170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2104116742Ssam 2105170530Ssam ieee80211_add_callback(m, ieee80211_tx_mgt_cb, 2106178354Ssam (void *) vap->iv_state); 2107116742Ssam break; 2108116742Ssam 2109116742Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 2110116742Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 2111116742Ssam /* 2112170530Ssam * asresp frame format 2113116742Ssam * [2] capability information 2114116742Ssam * [2] status 2115116742Ssam * [2] association ID 2116116742Ssam * [tlv] supported rates 2117116742Ssam * [tlv] extended supported rates 2118178354Ssam * [tlv] HT capabilities (standard, if STA enabled) 2119178354Ssam * [tlv] HT information (standard, if STA enabled) 2120178354Ssam * [tlv] WME (if configured and STA enabled) 2121178354Ssam * [tlv] HT capabilities (vendor OUI, if STA enabled) 2122178354Ssam * [tlv] HT information (vendor OUI, if STA enabled) 2123178354Ssam * [tlv] Atheros capabilities (if STA enabled) 2124178354Ssam * [tlv] AppIE's (optional) 2125116742Ssam */ 2126138568Ssam m = ieee80211_getmgtframe(&frm, 2127170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 2128170530Ssam sizeof(uint16_t) 2129170530Ssam + sizeof(uint16_t) 2130170530Ssam + sizeof(uint16_t) 2131116742Ssam + 2 + IEEE80211_RATE_SIZE 2132138568Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 2133170530Ssam + sizeof(struct ieee80211_ie_htcap) + 4 2134170530Ssam + sizeof(struct ieee80211_ie_htinfo) + 4 2135178354Ssam + sizeof(struct ieee80211_wme_param) 2136190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2137170530Ssam + sizeof(struct ieee80211_ath_ie) 2138190391Ssam#endif 2139178354Ssam + (vap->iv_appie_assocresp != NULL ? 2140178354Ssam vap->iv_appie_assocresp->ie_len : 0) 2141138568Ssam ); 2142116742Ssam if (m == NULL) 2143138568Ssam senderr(ENOMEM, is_tx_nobuf); 2144116742Ssam 2145195618Srpaulo capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); 2146170530Ssam *(uint16_t *)frm = htole16(capinfo); 2147116742Ssam frm += 2; 2148116742Ssam 2149170530Ssam *(uint16_t *)frm = htole16(arg); /* status */ 2150116742Ssam frm += 2; 2151116742Ssam 2152138568Ssam if (arg == IEEE80211_STATUS_SUCCESS) { 2153170530Ssam *(uint16_t *)frm = htole16(ni->ni_associd); 2154138568Ssam IEEE80211_NODE_STAT(ni, tx_assoc); 2155138568Ssam } else 2156138568Ssam IEEE80211_NODE_STAT(ni, tx_assoc_fail); 2157116742Ssam frm += 2; 2158116742Ssam 2159119150Ssam frm = ieee80211_add_rates(frm, &ni->ni_rates); 2160119150Ssam frm = ieee80211_add_xrates(frm, &ni->ni_rates); 2161173273Ssam /* NB: respond according to what we received */ 2162173273Ssam if ((ni->ni_flags & HTFLAGS) == IEEE80211_NODE_HT) { 2163173273Ssam frm = ieee80211_add_htcap(frm, ni); 2164173273Ssam frm = ieee80211_add_htinfo(frm, ni); 2165173273Ssam } 2166178354Ssam if ((vap->iv_flags & IEEE80211_F_WME) && 2167178354Ssam ni->ni_ies.wme_ie != NULL) 2168138568Ssam frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 2169173273Ssam if ((ni->ni_flags & HTFLAGS) == HTFLAGS) { 2170173273Ssam frm = ieee80211_add_htcap_vendor(frm, ni); 2171173273Ssam frm = ieee80211_add_htinfo_vendor(frm, ni); 2172170530Ssam } 2173190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2174178354Ssam if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) 2175190451Ssam frm = ieee80211_add_ath(frm, 2176178354Ssam IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), 2177190451Ssam ((vap->iv_flags & IEEE80211_F_WPA) == 0 && 2178190451Ssam ni->ni_authmode != IEEE80211_AUTH_8021X) ? 2179190451Ssam vap->iv_def_txkey : IEEE80211_KEYIX_NONE); 2180190391Ssam#endif /* IEEE80211_SUPPORT_SUPERG */ 2181178354Ssam if (vap->iv_appie_assocresp != NULL) 2182178354Ssam frm = add_appie(frm, vap->iv_appie_assocresp); 2183170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2184116742Ssam break; 2185116742Ssam 2186116742Ssam case IEEE80211_FC0_SUBTYPE_DISASSOC: 2187178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, 2188178354Ssam "send station disassociate (reason %d)", arg); 2189170530Ssam m = ieee80211_getmgtframe(&frm, 2190170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 2191170530Ssam sizeof(uint16_t)); 2192116742Ssam if (m == NULL) 2193138568Ssam senderr(ENOMEM, is_tx_nobuf); 2194170530Ssam *(uint16_t *)frm = htole16(arg); /* reason */ 2195170530Ssam m->m_pkthdr.len = m->m_len = sizeof(uint16_t); 2196138568Ssam 2197138568Ssam IEEE80211_NODE_STAT(ni, tx_disassoc); 2198138568Ssam IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); 2199116742Ssam break; 2200116742Ssam 2201116742Ssam default: 2202178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, 2203178354Ssam "invalid mgmt frame type %u", type); 2204121180Ssam senderr(EINVAL, is_tx_unknownmgt); 2205119150Ssam /* NOTREACHED */ 2206116742Ssam } 2207170530Ssam 2208184282Ssam /* NB: force non-ProbeResp frames to the highest queue */ 2209184282Ssam params.ibp_pri = WME_AC_VO; 2210184282Ssam params.ibp_rate0 = bss->ni_txparms->mgmtrate; 2211184282Ssam /* NB: we know all frames are unicast */ 2212184282Ssam params.ibp_try0 = bss->ni_txparms->maxretry; 2213184282Ssam params.ibp_power = bss->ni_txpower; 2214184282Ssam return ieee80211_mgmt_output(ni, m, type, ¶ms); 2215119150Ssambad: 2216170530Ssam ieee80211_free_node(ni); 2217116742Ssam return ret; 2218119150Ssam#undef senderr 2219173273Ssam#undef HTFLAGS 2220116742Ssam} 2221138568Ssam 2222178354Ssam/* 2223178354Ssam * Return an mbuf with a probe response frame in it. 2224178354Ssam * Space is left to prepend and 802.11 header at the 2225178354Ssam * front but it's left to the caller to fill in. 2226178354Ssam */ 2227178354Ssamstruct mbuf * 2228178354Ssamieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) 2229178354Ssam{ 2230178354Ssam struct ieee80211vap *vap = bss->ni_vap; 2231178354Ssam struct ieee80211com *ic = bss->ni_ic; 2232178354Ssam const struct ieee80211_rateset *rs; 2233178354Ssam struct mbuf *m; 2234178354Ssam uint16_t capinfo; 2235178354Ssam uint8_t *frm; 2236178354Ssam 2237178354Ssam /* 2238178354Ssam * probe response frame format 2239178354Ssam * [8] time stamp 2240178354Ssam * [2] beacon interval 2241178354Ssam * [2] cabability information 2242178354Ssam * [tlv] ssid 2243178354Ssam * [tlv] supported rates 2244178354Ssam * [tlv] parameter set (FH/DS) 2245178354Ssam * [tlv] parameter set (IBSS) 2246178354Ssam * [tlv] country (optional) 2247178354Ssam * [3] power control (optional) 2248178354Ssam * [5] channel switch announcement (CSA) (optional) 2249178354Ssam * [tlv] extended rate phy (ERP) 2250178354Ssam * [tlv] extended supported rates 2251180351Ssam * [tlv] RSN (optional) 2252178354Ssam * [tlv] HT capabilities 2253178354Ssam * [tlv] HT information 2254178354Ssam * [tlv] WPA (optional) 2255178354Ssam * [tlv] WME (optional) 2256178354Ssam * [tlv] Vendor OUI HT capabilities (optional) 2257178354Ssam * [tlv] Vendor OUI HT information (optional) 2258178354Ssam * [tlv] Atheros capabilities 2259178354Ssam * [tlv] AppIE's (optional) 2260195618Srpaulo * [tlv] Mesh ID (MBSS) 2261195618Srpaulo * [tlv] Mesh Conf (MBSS) 2262178354Ssam */ 2263178354Ssam m = ieee80211_getmgtframe(&frm, 2264178354Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 2265178354Ssam 8 2266178354Ssam + sizeof(uint16_t) 2267178354Ssam + sizeof(uint16_t) 2268178354Ssam + 2 + IEEE80211_NWID_LEN 2269178354Ssam + 2 + IEEE80211_RATE_SIZE 2270178354Ssam + 7 /* max(7,3) */ 2271178354Ssam + IEEE80211_COUNTRY_MAX_SIZE 2272178354Ssam + 3 2273178354Ssam + sizeof(struct ieee80211_csa_ie) 2274178354Ssam + 3 2275178354Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 2276180351Ssam + sizeof(struct ieee80211_ie_wpa) 2277178354Ssam + sizeof(struct ieee80211_ie_htcap) 2278178354Ssam + sizeof(struct ieee80211_ie_htinfo) 2279178354Ssam + sizeof(struct ieee80211_ie_wpa) 2280178354Ssam + sizeof(struct ieee80211_wme_param) 2281178354Ssam + 4 + sizeof(struct ieee80211_ie_htcap) 2282178354Ssam + 4 + sizeof(struct ieee80211_ie_htinfo) 2283190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2284178354Ssam + sizeof(struct ieee80211_ath_ie) 2285190391Ssam#endif 2286195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 2287195618Srpaulo + 2 + IEEE80211_MESHID_LEN 2288195618Srpaulo + sizeof(struct ieee80211_meshconf_ie) 2289195618Srpaulo#endif 2290178354Ssam + (vap->iv_appie_proberesp != NULL ? 2291178354Ssam vap->iv_appie_proberesp->ie_len : 0) 2292178354Ssam ); 2293178354Ssam if (m == NULL) { 2294178354Ssam vap->iv_stats.is_tx_nobuf++; 2295178354Ssam return NULL; 2296178354Ssam } 2297178354Ssam 2298178354Ssam memset(frm, 0, 8); /* timestamp should be filled later */ 2299178354Ssam frm += 8; 2300178354Ssam *(uint16_t *)frm = htole16(bss->ni_intval); 2301178354Ssam frm += 2; 2302195618Srpaulo capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); 2303178354Ssam *(uint16_t *)frm = htole16(capinfo); 2304178354Ssam frm += 2; 2305178354Ssam 2306178354Ssam frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen); 2307178354Ssam rs = ieee80211_get_suprates(ic, bss->ni_chan); 2308178354Ssam frm = ieee80211_add_rates(frm, rs); 2309178354Ssam 2310178354Ssam if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) { 2311178354Ssam *frm++ = IEEE80211_ELEMID_FHPARMS; 2312178354Ssam *frm++ = 5; 2313178354Ssam *frm++ = bss->ni_fhdwell & 0x00ff; 2314178354Ssam *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff; 2315178354Ssam *frm++ = IEEE80211_FH_CHANSET( 2316178354Ssam ieee80211_chan2ieee(ic, bss->ni_chan)); 2317178354Ssam *frm++ = IEEE80211_FH_CHANPAT( 2318178354Ssam ieee80211_chan2ieee(ic, bss->ni_chan)); 2319178354Ssam *frm++ = bss->ni_fhindex; 2320178354Ssam } else { 2321178354Ssam *frm++ = IEEE80211_ELEMID_DSPARMS; 2322178354Ssam *frm++ = 1; 2323178354Ssam *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan); 2324178354Ssam } 2325178354Ssam 2326178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) { 2327178354Ssam *frm++ = IEEE80211_ELEMID_IBSSPARMS; 2328178354Ssam *frm++ = 2; 2329178354Ssam *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 2330178354Ssam } 2331178354Ssam if ((vap->iv_flags & IEEE80211_F_DOTH) || 2332178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) 2333178354Ssam frm = ieee80211_add_countryie(frm, ic); 2334178354Ssam if (vap->iv_flags & IEEE80211_F_DOTH) { 2335178354Ssam if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan)) 2336178354Ssam frm = ieee80211_add_powerconstraint(frm, vap); 2337178354Ssam if (ic->ic_flags & IEEE80211_F_CSAPENDING) 2338178354Ssam frm = ieee80211_add_csa(frm, vap); 2339178354Ssam } 2340178354Ssam if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan)) 2341178354Ssam frm = ieee80211_add_erp(frm, ic); 2342178354Ssam frm = ieee80211_add_xrates(frm, rs); 2343266507Shselasky frm = ieee80211_add_rsn(frm, vap); 2344178354Ssam /* 2345178354Ssam * NB: legacy 11b clients do not get certain ie's. 2346178354Ssam * The caller identifies such clients by passing 2347178354Ssam * a token in legacy to us. Could expand this to be 2348178354Ssam * any legacy client for stuff like HT ie's. 2349178354Ssam */ 2350178354Ssam if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && 2351178354Ssam legacy != IEEE80211_SEND_LEGACY_11B) { 2352178354Ssam frm = ieee80211_add_htcap(frm, bss); 2353178354Ssam frm = ieee80211_add_htinfo(frm, bss); 2354178354Ssam } 2355266507Shselasky frm = ieee80211_add_wpa(frm, vap); 2356178354Ssam if (vap->iv_flags & IEEE80211_F_WME) 2357178354Ssam frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 2358178354Ssam if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && 2359193655Ssam (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) && 2360178354Ssam legacy != IEEE80211_SEND_LEGACY_11B) { 2361178354Ssam frm = ieee80211_add_htcap_vendor(frm, bss); 2362178354Ssam frm = ieee80211_add_htinfo_vendor(frm, bss); 2363178354Ssam } 2364190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2365190451Ssam if ((vap->iv_flags & IEEE80211_F_ATHEROS) && 2366190451Ssam legacy != IEEE80211_SEND_LEGACY_11B) 2367190451Ssam frm = ieee80211_add_athcaps(frm, bss); 2368190391Ssam#endif 2369178354Ssam if (vap->iv_appie_proberesp != NULL) 2370178354Ssam frm = add_appie(frm, vap->iv_appie_proberesp); 2371195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 2372195618Srpaulo if (vap->iv_opmode == IEEE80211_M_MBSS) { 2373195618Srpaulo frm = ieee80211_add_meshid(frm, vap); 2374195618Srpaulo frm = ieee80211_add_meshconf(frm, vap); 2375195618Srpaulo } 2376195618Srpaulo#endif 2377178354Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2378178354Ssam 2379178354Ssam return m; 2380178354Ssam} 2381178354Ssam 2382178354Ssam/* 2383178354Ssam * Send a probe response frame to the specified mac address. 2384178354Ssam * This does not go through the normal mgt frame api so we 2385178354Ssam * can specify the destination address and re-use the bss node 2386178354Ssam * for the sta reference. 2387178354Ssam */ 2388178354Ssamint 2389178354Ssamieee80211_send_proberesp(struct ieee80211vap *vap, 2390178354Ssam const uint8_t da[IEEE80211_ADDR_LEN], int legacy) 2391178354Ssam{ 2392178354Ssam struct ieee80211_node *bss = vap->iv_bss; 2393178354Ssam struct ieee80211com *ic = vap->iv_ic; 2394178354Ssam struct ieee80211_frame *wh; 2395178354Ssam struct mbuf *m; 2396178354Ssam 2397178354Ssam if (vap->iv_state == IEEE80211_S_CAC) { 2398178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, 2399178354Ssam "block %s frame in CAC state", "probe response"); 2400178354Ssam vap->iv_stats.is_tx_badstate++; 2401178354Ssam return EIO; /* XXX */ 2402178354Ssam } 2403178354Ssam 2404178354Ssam /* 2405178354Ssam * Hold a reference on the node so it doesn't go away until after 2406178354Ssam * the xmit is complete all the way in the driver. On error we 2407178354Ssam * will remove our reference. 2408178354Ssam */ 2409178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 2410178354Ssam "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 2411178354Ssam __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr), 2412178354Ssam ieee80211_node_refcnt(bss)+1); 2413178354Ssam ieee80211_ref_node(bss); 2414178354Ssam 2415178354Ssam m = ieee80211_alloc_proberesp(bss, legacy); 2416178354Ssam if (m == NULL) { 2417178354Ssam ieee80211_free_node(bss); 2418178354Ssam return ENOMEM; 2419178354Ssam } 2420178354Ssam 2421178354Ssam M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 2422178354Ssam KASSERT(m != NULL, ("no room for header")); 2423178354Ssam 2424178354Ssam wh = mtod(m, struct ieee80211_frame *); 2425191544Ssam ieee80211_send_setup(bss, m, 2426184282Ssam IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, 2427184282Ssam IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid); 2428178354Ssam /* XXX power management? */ 2429184286Ssam m->m_flags |= M_ENCAP; /* mark encapsulated */ 2430178354Ssam 2431178354Ssam M_WME_SETAC(m, WME_AC_BE); 2432178354Ssam 2433178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 2434178354Ssam "send probe resp on channel %u to %s%s\n", 2435178354Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da), 2436178354Ssam legacy ? " <legacy>" : ""); 2437178354Ssam IEEE80211_NODE_STAT(bss, tx_mgmt); 2438178354Ssam 2439178354Ssam return ic->ic_raw_xmit(bss, m, NULL); 2440178354Ssam} 2441178354Ssam 2442178354Ssam/* 2443178354Ssam * Allocate and build a RTS (Request To Send) control frame. 2444178354Ssam */ 2445178354Ssamstruct mbuf * 2446178354Ssamieee80211_alloc_rts(struct ieee80211com *ic, 2447178354Ssam const uint8_t ra[IEEE80211_ADDR_LEN], 2448178354Ssam const uint8_t ta[IEEE80211_ADDR_LEN], 2449178354Ssam uint16_t dur) 2450178354Ssam{ 2451178354Ssam struct ieee80211_frame_rts *rts; 2452178354Ssam struct mbuf *m; 2453178354Ssam 2454178354Ssam /* XXX honor ic_headroom */ 2455178354Ssam m = m_gethdr(M_DONTWAIT, MT_DATA); 2456178354Ssam if (m != NULL) { 2457178354Ssam rts = mtod(m, struct ieee80211_frame_rts *); 2458178354Ssam rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | 2459178354Ssam IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS; 2460178354Ssam rts->i_fc[1] = IEEE80211_FC1_DIR_NODS; 2461178354Ssam *(u_int16_t *)rts->i_dur = htole16(dur); 2462178354Ssam IEEE80211_ADDR_COPY(rts->i_ra, ra); 2463178354Ssam IEEE80211_ADDR_COPY(rts->i_ta, ta); 2464178354Ssam 2465178354Ssam m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); 2466178354Ssam } 2467178354Ssam return m; 2468178354Ssam} 2469178354Ssam 2470178354Ssam/* 2471178354Ssam * Allocate and build a CTS (Clear To Send) control frame. 2472178354Ssam */ 2473178354Ssamstruct mbuf * 2474178354Ssamieee80211_alloc_cts(struct ieee80211com *ic, 2475178354Ssam const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur) 2476178354Ssam{ 2477178354Ssam struct ieee80211_frame_cts *cts; 2478178354Ssam struct mbuf *m; 2479178354Ssam 2480178354Ssam /* XXX honor ic_headroom */ 2481178354Ssam m = m_gethdr(M_DONTWAIT, MT_DATA); 2482178354Ssam if (m != NULL) { 2483178354Ssam cts = mtod(m, struct ieee80211_frame_cts *); 2484178354Ssam cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | 2485178354Ssam IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS; 2486178354Ssam cts->i_fc[1] = IEEE80211_FC1_DIR_NODS; 2487178354Ssam *(u_int16_t *)cts->i_dur = htole16(dur); 2488178354Ssam IEEE80211_ADDR_COPY(cts->i_ra, ra); 2489178354Ssam 2490178354Ssam m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); 2491178354Ssam } 2492178354Ssam return m; 2493178354Ssam} 2494178354Ssam 2495170530Ssamstatic void 2496170530Ssamieee80211_tx_mgt_timeout(void *arg) 2497170530Ssam{ 2498170530Ssam struct ieee80211_node *ni = arg; 2499178354Ssam struct ieee80211vap *vap = ni->ni_vap; 2500170530Ssam 2501178354Ssam if (vap->iv_state != IEEE80211_S_INIT && 2502178354Ssam (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) { 2503170530Ssam /* 2504170530Ssam * NB: it's safe to specify a timeout as the reason here; 2505170530Ssam * it'll only be used in the right state. 2506170530Ssam */ 2507178354Ssam ieee80211_new_state(vap, IEEE80211_S_SCAN, 2508170530Ssam IEEE80211_SCAN_FAIL_TIMEOUT); 2509170530Ssam } 2510170530Ssam} 2511170530Ssam 2512170530Ssamstatic void 2513170530Ssamieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) 2514170530Ssam{ 2515178354Ssam struct ieee80211vap *vap = ni->ni_vap; 2516170530Ssam enum ieee80211_state ostate = (enum ieee80211_state) arg; 2517170530Ssam 2518170530Ssam /* 2519170530Ssam * Frame transmit completed; arrange timer callback. If 2520170530Ssam * transmit was successfuly we wait for response. Otherwise 2521170530Ssam * we arrange an immediate callback instead of doing the 2522170530Ssam * callback directly since we don't know what state the driver 2523170530Ssam * is in (e.g. what locks it is holding). This work should 2524170530Ssam * not be too time-critical and not happen too often so the 2525170530Ssam * added overhead is acceptable. 2526170530Ssam * 2527170530Ssam * XXX what happens if !acked but response shows up before callback? 2528170530Ssam */ 2529178354Ssam if (vap->iv_state == ostate) 2530178354Ssam callout_reset(&vap->iv_mgtsend, 2531170530Ssam status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, 2532170530Ssam ieee80211_tx_mgt_timeout, ni); 2533170530Ssam} 2534170530Ssam 2535178354Ssamstatic void 2536178354Ssamieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, 2537178354Ssam struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni) 2538138568Ssam{ 2539178354Ssam struct ieee80211vap *vap = ni->ni_vap; 2540172211Ssam struct ieee80211com *ic = ni->ni_ic; 2541178354Ssam struct ieee80211_rateset *rs = &ni->ni_rates; 2542170530Ssam uint16_t capinfo; 2543138568Ssam 2544138568Ssam /* 2545138568Ssam * beacon frame format 2546138568Ssam * [8] time stamp 2547138568Ssam * [2] beacon interval 2548138568Ssam * [2] cabability information 2549138568Ssam * [tlv] ssid 2550138568Ssam * [tlv] supported rates 2551138568Ssam * [3] parameter set (DS) 2552178354Ssam * [8] CF parameter set (optional) 2553138568Ssam * [tlv] parameter set (IBSS/TIM) 2554178354Ssam * [tlv] country (optional) 2555178354Ssam * [3] power control (optional) 2556178354Ssam * [5] channel switch announcement (CSA) (optional) 2557138568Ssam * [tlv] extended rate phy (ERP) 2558138568Ssam * [tlv] extended supported rates 2559180351Ssam * [tlv] RSN parameters 2560170530Ssam * [tlv] HT capabilities 2561170530Ssam * [tlv] HT information 2562178354Ssam * XXX Vendor-specific OIDs (e.g. Atheros) 2563178354Ssam * [tlv] WPA parameters 2564178354Ssam * [tlv] WME parameters 2565170530Ssam * [tlv] Vendor OUI HT capabilities (optional) 2566170530Ssam * [tlv] Vendor OUI HT information (optional) 2567190451Ssam * [tlv] Atheros capabilities (optional) 2568186904Ssam * [tlv] TDMA parameters (optional) 2569195618Srpaulo * [tlv] Mesh ID (MBSS) 2570195618Srpaulo * [tlv] Mesh Conf (MBSS) 2571178354Ssam * [tlv] application data (optional) 2572138568Ssam */ 2573138568Ssam 2574173273Ssam memset(bo, 0, sizeof(*bo)); 2575173273Ssam 2576138568Ssam memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ 2577138568Ssam frm += 8; 2578170530Ssam *(uint16_t *)frm = htole16(ni->ni_intval); 2579138568Ssam frm += 2; 2580195618Srpaulo capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); 2581170530Ssam bo->bo_caps = (uint16_t *)frm; 2582170530Ssam *(uint16_t *)frm = htole16(capinfo); 2583138568Ssam frm += 2; 2584138568Ssam *frm++ = IEEE80211_ELEMID_SSID; 2585178354Ssam if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) { 2586138568Ssam *frm++ = ni->ni_esslen; 2587138568Ssam memcpy(frm, ni->ni_essid, ni->ni_esslen); 2588138568Ssam frm += ni->ni_esslen; 2589138568Ssam } else 2590138568Ssam *frm++ = 0; 2591138568Ssam frm = ieee80211_add_rates(frm, rs); 2592178354Ssam if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) { 2593138568Ssam *frm++ = IEEE80211_ELEMID_DSPARMS; 2594138568Ssam *frm++ = 1; 2595178354Ssam *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); 2596138568Ssam } 2597178354Ssam if (ic->ic_flags & IEEE80211_F_PCF) { 2598178354Ssam bo->bo_cfp = frm; 2599178354Ssam frm = ieee80211_add_cfparms(frm, ic); 2600178354Ssam } 2601138568Ssam bo->bo_tim = frm; 2602178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) { 2603138568Ssam *frm++ = IEEE80211_ELEMID_IBSSPARMS; 2604138568Ssam *frm++ = 2; 2605138568Ssam *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 2606138568Ssam bo->bo_tim_len = 0; 2607195618Srpaulo } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || 2608195618Srpaulo vap->iv_opmode == IEEE80211_M_MBSS) { 2609195618Srpaulo /* TIM IE is the same for Mesh and Hostap */ 2610138568Ssam struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; 2611138568Ssam 2612138568Ssam tie->tim_ie = IEEE80211_ELEMID_TIM; 2613138568Ssam tie->tim_len = 4; /* length */ 2614138568Ssam tie->tim_count = 0; /* DTIM count */ 2615178354Ssam tie->tim_period = vap->iv_dtim_period; /* DTIM period */ 2616138568Ssam tie->tim_bitctl = 0; /* bitmap control */ 2617138568Ssam tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ 2618138568Ssam frm += sizeof(struct ieee80211_tim_ie); 2619138568Ssam bo->bo_tim_len = 1; 2620138568Ssam } 2621172211Ssam bo->bo_tim_trailer = frm; 2622178354Ssam if ((vap->iv_flags & IEEE80211_F_DOTH) || 2623178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) 2624178354Ssam frm = ieee80211_add_countryie(frm, ic); 2625178354Ssam if (vap->iv_flags & IEEE80211_F_DOTH) { 2626178354Ssam if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) 2627178354Ssam frm = ieee80211_add_powerconstraint(frm, vap); 2628178354Ssam bo->bo_csa = frm; 2629178354Ssam if (ic->ic_flags & IEEE80211_F_CSAPENDING) 2630178354Ssam frm = ieee80211_add_csa(frm, vap); 2631178354Ssam } else 2632178354Ssam bo->bo_csa = frm; 2633178354Ssam if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { 2634153973Ssam bo->bo_erp = frm; 2635138568Ssam frm = ieee80211_add_erp(frm, ic); 2636173273Ssam } 2637170530Ssam frm = ieee80211_add_xrates(frm, rs); 2638266507Shselasky frm = ieee80211_add_rsn(frm, vap); 2639178354Ssam if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { 2640170530Ssam frm = ieee80211_add_htcap(frm, ni); 2641170530Ssam bo->bo_htinfo = frm; 2642170530Ssam frm = ieee80211_add_htinfo(frm, ni); 2643173273Ssam } 2644266507Shselasky frm = ieee80211_add_wpa(frm, vap); 2645178354Ssam if (vap->iv_flags & IEEE80211_F_WME) { 2646173273Ssam bo->bo_wme = frm; 2647173273Ssam frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 2648173273Ssam } 2649178354Ssam if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && 2650193655Ssam (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT)) { 2651173273Ssam frm = ieee80211_add_htcap_vendor(frm, ni); 2652173273Ssam frm = ieee80211_add_htinfo_vendor(frm, ni); 2653173273Ssam } 2654190451Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2655190451Ssam if (vap->iv_flags & IEEE80211_F_ATHEROS) { 2656190451Ssam bo->bo_ath = frm; 2657190451Ssam frm = ieee80211_add_athcaps(frm, ni); 2658190451Ssam } 2659190451Ssam#endif 2660186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 2661186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) { 2662186904Ssam bo->bo_tdma = frm; 2663186904Ssam frm = ieee80211_add_tdma(frm, vap); 2664186904Ssam } 2665186904Ssam#endif 2666178354Ssam if (vap->iv_appie_beacon != NULL) { 2667178354Ssam bo->bo_appie = frm; 2668178354Ssam bo->bo_appie_len = vap->iv_appie_beacon->ie_len; 2669178354Ssam frm = add_appie(frm, vap->iv_appie_beacon); 2670178354Ssam } 2671195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 2672195618Srpaulo if (vap->iv_opmode == IEEE80211_M_MBSS) { 2673195618Srpaulo frm = ieee80211_add_meshid(frm, vap); 2674198242Srpaulo bo->bo_meshconf = frm; 2675195618Srpaulo frm = ieee80211_add_meshconf(frm, vap); 2676195618Srpaulo } 2677195618Srpaulo#endif 2678172211Ssam bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer; 2679178354Ssam bo->bo_csa_trailer_len = frm - bo->bo_csa; 2680170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2681178354Ssam} 2682138568Ssam 2683178354Ssam/* 2684178354Ssam * Allocate a beacon frame and fillin the appropriate bits. 2685178354Ssam */ 2686178354Ssamstruct mbuf * 2687178354Ssamieee80211_beacon_alloc(struct ieee80211_node *ni, 2688178354Ssam struct ieee80211_beacon_offsets *bo) 2689178354Ssam{ 2690178354Ssam struct ieee80211vap *vap = ni->ni_vap; 2691178354Ssam struct ieee80211com *ic = ni->ni_ic; 2692178354Ssam struct ifnet *ifp = vap->iv_ifp; 2693178354Ssam struct ieee80211_frame *wh; 2694178354Ssam struct mbuf *m; 2695178354Ssam int pktlen; 2696178354Ssam uint8_t *frm; 2697178354Ssam 2698178354Ssam /* 2699178354Ssam * beacon frame format 2700178354Ssam * [8] time stamp 2701178354Ssam * [2] beacon interval 2702178354Ssam * [2] cabability information 2703178354Ssam * [tlv] ssid 2704178354Ssam * [tlv] supported rates 2705178354Ssam * [3] parameter set (DS) 2706178354Ssam * [8] CF parameter set (optional) 2707178354Ssam * [tlv] parameter set (IBSS/TIM) 2708178354Ssam * [tlv] country (optional) 2709178354Ssam * [3] power control (optional) 2710178354Ssam * [5] channel switch announcement (CSA) (optional) 2711178354Ssam * [tlv] extended rate phy (ERP) 2712178354Ssam * [tlv] extended supported rates 2713178354Ssam * [tlv] RSN parameters 2714178354Ssam * [tlv] HT capabilities 2715178354Ssam * [tlv] HT information 2716178354Ssam * [tlv] Vendor OUI HT capabilities (optional) 2717178354Ssam * [tlv] Vendor OUI HT information (optional) 2718178354Ssam * XXX Vendor-specific OIDs (e.g. Atheros) 2719178354Ssam * [tlv] WPA parameters 2720178354Ssam * [tlv] WME parameters 2721186904Ssam * [tlv] TDMA parameters (optional) 2722195618Srpaulo * [tlv] Mesh ID (MBSS) 2723195618Srpaulo * [tlv] Mesh Conf (MBSS) 2724178354Ssam * [tlv] application data (optional) 2725178354Ssam * NB: we allocate the max space required for the TIM bitmap. 2726178354Ssam * XXX how big is this? 2727178354Ssam */ 2728178354Ssam pktlen = 8 /* time stamp */ 2729178354Ssam + sizeof(uint16_t) /* beacon interval */ 2730178354Ssam + sizeof(uint16_t) /* capabilities */ 2731178354Ssam + 2 + ni->ni_esslen /* ssid */ 2732178354Ssam + 2 + IEEE80211_RATE_SIZE /* supported rates */ 2733178354Ssam + 2 + 1 /* DS parameters */ 2734178354Ssam + 2 + 6 /* CF parameters */ 2735178354Ssam + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ 2736178354Ssam + IEEE80211_COUNTRY_MAX_SIZE /* country */ 2737178354Ssam + 2 + 1 /* power control */ 2738178354Ssam + sizeof(struct ieee80211_csa_ie) /* CSA */ 2739178354Ssam + 2 + 1 /* ERP */ 2740178354Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 2741178354Ssam + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ 2742178354Ssam 2*sizeof(struct ieee80211_ie_wpa) : 0) 2743178354Ssam /* XXX conditional? */ 2744178354Ssam + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ 2745178354Ssam + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ 2746178354Ssam + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ 2747178354Ssam sizeof(struct ieee80211_wme_param) : 0) 2748190451Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2749190451Ssam + sizeof(struct ieee80211_ath_ie) /* ATH */ 2750190451Ssam#endif 2751186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 2752186904Ssam + (vap->iv_caps & IEEE80211_C_TDMA ? /* TDMA */ 2753186904Ssam sizeof(struct ieee80211_tdma_param) : 0) 2754186904Ssam#endif 2755195618Srpaulo#ifdef IEEE80211_SUPPORT_MESH 2756195618Srpaulo + 2 + ni->ni_meshidlen 2757195618Srpaulo + sizeof(struct ieee80211_meshconf_ie) 2758195618Srpaulo#endif 2759178354Ssam + IEEE80211_MAX_APPIE 2760178354Ssam ; 2761178354Ssam m = ieee80211_getmgtframe(&frm, 2762178354Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); 2763178354Ssam if (m == NULL) { 2764178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, 2765178354Ssam "%s: cannot get buf; size %u\n", __func__, pktlen); 2766178354Ssam vap->iv_stats.is_tx_nobuf++; 2767178354Ssam return NULL; 2768178354Ssam } 2769178354Ssam ieee80211_beacon_construct(m, frm, bo, ni); 2770178354Ssam 2771138568Ssam M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 2772138568Ssam KASSERT(m != NULL, ("no space for 802.11 header?")); 2773138568Ssam wh = mtod(m, struct ieee80211_frame *); 2774138568Ssam wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 2775138568Ssam IEEE80211_FC0_SUBTYPE_BEACON; 2776138568Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 2777170530Ssam *(uint16_t *)wh->i_dur = 0; 2778138568Ssam IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 2779178354Ssam IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 2780198384Srpaulo IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 2781170530Ssam *(uint16_t *)wh->i_seq = 0; 2782138568Ssam 2783138568Ssam return m; 2784138568Ssam} 2785138568Ssam 2786138568Ssam/* 2787138568Ssam * Update the dynamic parts of a beacon frame based on the current state. 2788138568Ssam */ 2789138568Ssamint 2790172211Ssamieee80211_beacon_update(struct ieee80211_node *ni, 2791138568Ssam struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) 2792138568Ssam{ 2793178354Ssam struct ieee80211vap *vap = ni->ni_vap; 2794172211Ssam struct ieee80211com *ic = ni->ni_ic; 2795138568Ssam int len_changed = 0; 2796170530Ssam uint16_t capinfo; 2797225139Sadrian struct ieee80211_frame *wh; 2798225139Sadrian ieee80211_seq seqno; 2799138568Ssam 2800178354Ssam IEEE80211_LOCK(ic); 2801178354Ssam /* 2802178354Ssam * Handle 11h channel change when we've reached the count. 2803178354Ssam * We must recalculate the beacon frame contents to account 2804178354Ssam * for the new channel. Note we do this only for the first 2805178354Ssam * vap that reaches this point; subsequent vaps just update 2806178354Ssam * their beacon state to reflect the recalculated channel. 2807178354Ssam */ 2808178354Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) && 2809178354Ssam vap->iv_csa_count == ic->ic_csa_count) { 2810178354Ssam vap->iv_csa_count = 0; 2811178354Ssam /* 2812178354Ssam * Effect channel change before reconstructing the beacon 2813178354Ssam * frame contents as many places reference ni_chan. 2814178354Ssam */ 2815178354Ssam if (ic->ic_csa_newchan != NULL) 2816178354Ssam ieee80211_csa_completeswitch(ic); 2817178354Ssam /* 2818178354Ssam * NB: ieee80211_beacon_construct clears all pending 2819178354Ssam * updates in bo_flags so we don't need to explicitly 2820178354Ssam * clear IEEE80211_BEACON_CSA. 2821178354Ssam */ 2822178354Ssam ieee80211_beacon_construct(m, 2823178354Ssam mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni); 2824178354Ssam 2825178354Ssam /* XXX do WME aggressive mode processing? */ 2826178354Ssam IEEE80211_UNLOCK(ic); 2827178354Ssam return 1; /* just assume length changed */ 2828178354Ssam } 2829178354Ssam 2830225139Sadrian wh = mtod(m, struct ieee80211_frame *); 2831225139Sadrian seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; 2832225139Sadrian *(uint16_t *)&wh->i_seq[0] = 2833225139Sadrian htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 2834225139Sadrian M_SEQNO_SET(m, seqno); 2835225139Sadrian 2836138568Ssam /* XXX faster to recalculate entirely or just changes? */ 2837195618Srpaulo capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); 2838138568Ssam *bo->bo_caps = htole16(capinfo); 2839138568Ssam 2840178354Ssam if (vap->iv_flags & IEEE80211_F_WME) { 2841138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 2842138568Ssam 2843138568Ssam /* 2844138568Ssam * Check for agressive mode change. When there is 2845138568Ssam * significant high priority traffic in the BSS 2846138568Ssam * throttle back BE traffic by using conservative 2847138568Ssam * parameters. Otherwise BE uses agressive params 2848138568Ssam * to optimize performance of legacy/non-QoS traffic. 2849138568Ssam */ 2850138568Ssam if (wme->wme_flags & WME_F_AGGRMODE) { 2851138568Ssam if (wme->wme_hipri_traffic > 2852138568Ssam wme->wme_hipri_switch_thresh) { 2853178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 2854138568Ssam "%s: traffic %u, disable aggressive mode\n", 2855138568Ssam __func__, wme->wme_hipri_traffic); 2856138568Ssam wme->wme_flags &= ~WME_F_AGGRMODE; 2857178354Ssam ieee80211_wme_updateparams_locked(vap); 2858138568Ssam wme->wme_hipri_traffic = 2859138568Ssam wme->wme_hipri_switch_hysteresis; 2860138568Ssam } else 2861138568Ssam wme->wme_hipri_traffic = 0; 2862138568Ssam } else { 2863138568Ssam if (wme->wme_hipri_traffic <= 2864138568Ssam wme->wme_hipri_switch_thresh) { 2865178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 2866138568Ssam "%s: traffic %u, enable aggressive mode\n", 2867138568Ssam __func__, wme->wme_hipri_traffic); 2868138568Ssam wme->wme_flags |= WME_F_AGGRMODE; 2869178354Ssam ieee80211_wme_updateparams_locked(vap); 2870138568Ssam wme->wme_hipri_traffic = 0; 2871138568Ssam } else 2872138568Ssam wme->wme_hipri_traffic = 2873138568Ssam wme->wme_hipri_switch_hysteresis; 2874138568Ssam } 2875172211Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) { 2876138568Ssam (void) ieee80211_add_wme_param(bo->bo_wme, wme); 2877172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_WME); 2878138568Ssam } 2879138568Ssam } 2880138568Ssam 2881178354Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { 2882178354Ssam ieee80211_ht_update_beacon(vap, bo); 2883172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO); 2884170530Ssam } 2885186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 2886186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) { 2887186904Ssam /* 2888186904Ssam * NB: the beacon is potentially updated every TBTT. 2889186904Ssam */ 2890186904Ssam ieee80211_tdma_update_beacon(vap, bo); 2891186904Ssam } 2892186904Ssam#endif 2893198242Srpaulo#ifdef IEEE80211_SUPPORT_MESH 2894198242Srpaulo if (vap->iv_opmode == IEEE80211_M_MBSS) 2895198242Srpaulo ieee80211_mesh_update_beacon(vap, bo); 2896198242Srpaulo#endif 2897198242Srpaulo 2898195618Srpaulo if (vap->iv_opmode == IEEE80211_M_HOSTAP || 2899195618Srpaulo vap->iv_opmode == IEEE80211_M_MBSS) { /* NB: no IBSS support*/ 2900138568Ssam struct ieee80211_tim_ie *tie = 2901138568Ssam (struct ieee80211_tim_ie *) bo->bo_tim; 2902172211Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) { 2903138568Ssam u_int timlen, timoff, i; 2904138568Ssam /* 2905138568Ssam * ATIM/DTIM needs updating. If it fits in the 2906138568Ssam * current space allocated then just copy in the 2907138568Ssam * new bits. Otherwise we need to move any trailing 2908138568Ssam * data to make room. Note that we know there is 2909138568Ssam * contiguous space because ieee80211_beacon_allocate 2910138568Ssam * insures there is space in the mbuf to write a 2911178354Ssam * maximal-size virtual bitmap (based on iv_max_aid). 2912138568Ssam */ 2913138568Ssam /* 2914138568Ssam * Calculate the bitmap size and offset, copy any 2915138568Ssam * trailer out of the way, and then copy in the 2916138568Ssam * new bitmap and update the information element. 2917138568Ssam * Note that the tim bitmap must contain at least 2918138568Ssam * one byte and any offset must be even. 2919138568Ssam */ 2920178354Ssam if (vap->iv_ps_pending != 0) { 2921138568Ssam timoff = 128; /* impossibly large */ 2922178354Ssam for (i = 0; i < vap->iv_tim_len; i++) 2923178354Ssam if (vap->iv_tim_bitmap[i]) { 2924138568Ssam timoff = i &~ 1; 2925138568Ssam break; 2926138568Ssam } 2927138568Ssam KASSERT(timoff != 128, ("tim bitmap empty!")); 2928178354Ssam for (i = vap->iv_tim_len-1; i >= timoff; i--) 2929178354Ssam if (vap->iv_tim_bitmap[i]) 2930138568Ssam break; 2931138568Ssam timlen = 1 + (i - timoff); 2932138568Ssam } else { 2933138568Ssam timoff = 0; 2934138568Ssam timlen = 1; 2935138568Ssam } 2936138568Ssam if (timlen != bo->bo_tim_len) { 2937138568Ssam /* copy up/down trailer */ 2938153973Ssam int adjust = tie->tim_bitmap+timlen 2939172211Ssam - bo->bo_tim_trailer; 2940172211Ssam ovbcopy(bo->bo_tim_trailer, 2941172211Ssam bo->bo_tim_trailer+adjust, 2942172211Ssam bo->bo_tim_trailer_len); 2943172211Ssam bo->bo_tim_trailer += adjust; 2944153973Ssam bo->bo_erp += adjust; 2945170530Ssam bo->bo_htinfo += adjust; 2946218928Sbschmidt#ifdef IEEE80211_SUPPORT_SUPERG 2947190451Ssam bo->bo_ath += adjust; 2948190451Ssam#endif 2949218928Sbschmidt#ifdef IEEE80211_SUPPORT_TDMA 2950190448Ssam bo->bo_tdma += adjust; 2951190448Ssam#endif 2952218928Sbschmidt#ifdef IEEE80211_SUPPORT_MESH 2953198242Srpaulo bo->bo_meshconf += adjust; 2954198242Srpaulo#endif 2955178354Ssam bo->bo_appie += adjust; 2956178354Ssam bo->bo_wme += adjust; 2957178354Ssam bo->bo_csa += adjust; 2958138568Ssam bo->bo_tim_len = timlen; 2959138568Ssam 2960138568Ssam /* update information element */ 2961138568Ssam tie->tim_len = 3 + timlen; 2962138568Ssam tie->tim_bitctl = timoff; 2963138568Ssam len_changed = 1; 2964138568Ssam } 2965178354Ssam memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff, 2966138568Ssam bo->bo_tim_len); 2967138568Ssam 2968172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_TIM); 2969138568Ssam 2970178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, 2971138568Ssam "%s: TIM updated, pending %u, off %u, len %u\n", 2972178354Ssam __func__, vap->iv_ps_pending, timoff, timlen); 2973138568Ssam } 2974138568Ssam /* count down DTIM period */ 2975138568Ssam if (tie->tim_count == 0) 2976138568Ssam tie->tim_count = tie->tim_period - 1; 2977138568Ssam else 2978138568Ssam tie->tim_count--; 2979138568Ssam /* update state for buffered multicast frames on DTIM */ 2980153139Ssam if (mcast && tie->tim_count == 0) 2981138568Ssam tie->tim_bitctl |= 1; 2982138568Ssam else 2983138568Ssam tie->tim_bitctl &= ~1; 2984178354Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) { 2985178354Ssam struct ieee80211_csa_ie *csa = 2986178354Ssam (struct ieee80211_csa_ie *) bo->bo_csa; 2987178354Ssam 2988178354Ssam /* 2989178354Ssam * Insert or update CSA ie. If we're just starting 2990178354Ssam * to count down to the channel switch then we need 2991178354Ssam * to insert the CSA ie. Otherwise we just need to 2992178354Ssam * drop the count. The actual change happens above 2993178354Ssam * when the vap's count reaches the target count. 2994178354Ssam */ 2995178354Ssam if (vap->iv_csa_count == 0) { 2996178354Ssam memmove(&csa[1], csa, bo->bo_csa_trailer_len); 2997178354Ssam bo->bo_erp += sizeof(*csa); 2998190449Ssam bo->bo_htinfo += sizeof(*csa); 2999178354Ssam bo->bo_wme += sizeof(*csa); 3000218928Sbschmidt#ifdef IEEE80211_SUPPORT_SUPERG 3001190451Ssam bo->bo_ath += sizeof(*csa); 3002190451Ssam#endif 3003218928Sbschmidt#ifdef IEEE80211_SUPPORT_TDMA 3004190448Ssam bo->bo_tdma += sizeof(*csa); 3005190448Ssam#endif 3006218928Sbschmidt#ifdef IEEE80211_SUPPORT_MESH 3007198242Srpaulo bo->bo_meshconf += sizeof(*csa); 3008198242Srpaulo#endif 3009178354Ssam bo->bo_appie += sizeof(*csa); 3010178354Ssam bo->bo_csa_trailer_len += sizeof(*csa); 3011178354Ssam bo->bo_tim_trailer_len += sizeof(*csa); 3012178354Ssam m->m_len += sizeof(*csa); 3013178354Ssam m->m_pkthdr.len += sizeof(*csa); 3014178354Ssam 3015178354Ssam ieee80211_add_csa(bo->bo_csa, vap); 3016178354Ssam } else 3017178354Ssam csa->csa_count--; 3018178354Ssam vap->iv_csa_count++; 3019178354Ssam /* NB: don't clear IEEE80211_BEACON_CSA */ 3020178354Ssam } 3021172211Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { 3022153973Ssam /* 3023153973Ssam * ERP element needs updating. 3024153973Ssam */ 3025153973Ssam (void) ieee80211_add_erp(bo->bo_erp, ic); 3026172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_ERP); 3027153973Ssam } 3028190451Ssam#ifdef IEEE80211_SUPPORT_SUPERG 3029190451Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_ATH)) { 3030190451Ssam ieee80211_add_athcaps(bo->bo_ath, ni); 3031190451Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_ATH); 3032190451Ssam } 3033190451Ssam#endif 3034138568Ssam } 3035178354Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) { 3036178354Ssam const struct ieee80211_appie *aie = vap->iv_appie_beacon; 3037178354Ssam int aielen; 3038178354Ssam uint8_t *frm; 3039138568Ssam 3040178354Ssam aielen = 0; 3041178354Ssam if (aie != NULL) 3042178354Ssam aielen += aie->ie_len; 3043178354Ssam if (aielen != bo->bo_appie_len) { 3044178354Ssam /* copy up/down trailer */ 3045178354Ssam int adjust = aielen - bo->bo_appie_len; 3046178354Ssam ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, 3047178354Ssam bo->bo_tim_trailer_len); 3048178354Ssam bo->bo_tim_trailer += adjust; 3049178354Ssam bo->bo_appie += adjust; 3050178354Ssam bo->bo_appie_len = aielen; 3051178354Ssam 3052178354Ssam len_changed = 1; 3053178354Ssam } 3054178354Ssam frm = bo->bo_appie; 3055178354Ssam if (aie != NULL) 3056178354Ssam frm = add_appie(frm, aie); 3057178354Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE); 3058178354Ssam } 3059178354Ssam IEEE80211_UNLOCK(ic); 3060178354Ssam 3061138568Ssam return len_changed; 3062138568Ssam} 3063