ieee80211_output.c revision 172231
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3170360Ssam * Copyright (c) 2002-2007 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: head/sys/net80211/ieee80211_output.c 172231 2007-09-18 21:07:41Z sam $"); 29116742Ssam 30116742Ssam#include "opt_inet.h" 31116742Ssam 32116742Ssam#include <sys/param.h> 33116742Ssam#include <sys/systm.h> 34116742Ssam#include <sys/mbuf.h> 35116742Ssam#include <sys/kernel.h> 36116742Ssam#include <sys/endian.h> 37116742Ssam 38138568Ssam#include <sys/socket.h> 39116742Ssam 40138568Ssam#include <net/bpf.h> 41138568Ssam#include <net/ethernet.h> 42116742Ssam#include <net/if.h> 43138568Ssam#include <net/if_llc.h> 44116742Ssam#include <net/if_media.h> 45138568Ssam#include <net/if_vlan_var.h> 46116742Ssam 47116742Ssam#include <net80211/ieee80211_var.h> 48170530Ssam#include <net80211/ieee80211_regdomain.h> 49116742Ssam 50116742Ssam#ifdef INET 51116742Ssam#include <netinet/in.h> 52116742Ssam#include <netinet/if_ether.h> 53138568Ssam#include <netinet/in_systm.h> 54138568Ssam#include <netinet/ip.h> 55116742Ssam#endif 56116742Ssam 57170530Ssam#define ETHER_HEADER_COPY(dst, src) \ 58170530Ssam memcpy(dst, src, sizeof(struct ether_header)) 59170530Ssam 60170530Ssamstatic struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic, 61170530Ssam struct mbuf *m1, const struct ether_header *eh1, 62170530Ssam struct mbuf *m2, const struct ether_header *eh2); 63170530Ssamstatic int ieee80211_fragment(struct ieee80211com *, struct mbuf *, 64170530Ssam u_int hdrsize, u_int ciphdrsize, u_int mtu); 65170530Ssamstatic void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); 66170530Ssam 67138568Ssam#ifdef IEEE80211_DEBUG 68119150Ssam/* 69138568Ssam * Decide if an outbound management frame should be 70138568Ssam * printed when debugging is enabled. This filters some 71138568Ssam * of the less interesting frames that come frequently 72138568Ssam * (e.g. beacons). 73138568Ssam */ 74138568Ssamstatic __inline int 75138568Ssamdoprint(struct ieee80211com *ic, int subtype) 76138568Ssam{ 77138568Ssam switch (subtype) { 78138568Ssam case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 79138568Ssam return (ic->ic_opmode == IEEE80211_M_IBSS); 80138568Ssam } 81138568Ssam return 1; 82138568Ssam} 83138568Ssam#endif 84138568Ssam 85138568Ssam/* 86148314Ssam * Set the direction field and address fields of an outgoing 87148314Ssam * non-QoS frame. Note this should be called early on in 88148314Ssam * constructing a frame as it sets i_fc[1]; other bits can 89148314Ssam * then be or'd in. 90148314Ssam */ 91148314Ssamstatic void 92148314Ssamieee80211_send_setup(struct ieee80211com *ic, 93148314Ssam struct ieee80211_node *ni, 94148314Ssam struct ieee80211_frame *wh, 95148314Ssam int type, 96170530Ssam const uint8_t sa[IEEE80211_ADDR_LEN], 97170530Ssam const uint8_t da[IEEE80211_ADDR_LEN], 98170530Ssam const uint8_t bssid[IEEE80211_ADDR_LEN]) 99148314Ssam{ 100148314Ssam#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) 101148314Ssam 102148314Ssam wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; 103148314Ssam if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { 104148314Ssam switch (ic->ic_opmode) { 105148314Ssam case IEEE80211_M_STA: 106148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 107148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, bssid); 108148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, sa); 109148314Ssam IEEE80211_ADDR_COPY(wh->i_addr3, da); 110148314Ssam break; 111148314Ssam case IEEE80211_M_IBSS: 112148314Ssam case IEEE80211_M_AHDEMO: 113148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 114148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, da); 115148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, sa); 116148314Ssam IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 117148314Ssam break; 118148314Ssam case IEEE80211_M_HOSTAP: 119148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 120148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, da); 121148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, bssid); 122148314Ssam IEEE80211_ADDR_COPY(wh->i_addr3, sa); 123148314Ssam break; 124170530Ssam case IEEE80211_M_WDS: 125170530Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 126170530Ssam /* XXX cheat, bssid holds RA */ 127170530Ssam IEEE80211_ADDR_COPY(wh->i_addr1, bssid); 128170530Ssam IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 129170530Ssam IEEE80211_ADDR_COPY(wh->i_addr3, da); 130170530Ssam IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); 131170530Ssam break; 132148314Ssam case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ 133148314Ssam break; 134148314Ssam } 135148314Ssam } else { 136148314Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 137148314Ssam IEEE80211_ADDR_COPY(wh->i_addr1, da); 138148314Ssam IEEE80211_ADDR_COPY(wh->i_addr2, sa); 139148314Ssam IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 140148314Ssam } 141170530Ssam *(uint16_t *)&wh->i_dur[0] = 0; 142148314Ssam /* NB: use non-QoS tid */ 143170530Ssam *(uint16_t *)&wh->i_seq[0] = 144167439Ssam htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT); 145167439Ssam ni->ni_txseqs[IEEE80211_NONQOS_TID]++; 146148314Ssam#undef WH4 147148314Ssam} 148148314Ssam 149148314Ssam/* 150119150Ssam * Send a management frame to the specified node. The node pointer 151119150Ssam * must have a reference as the pointer will be passed to the driver 152119150Ssam * and potentially held for a long time. If the frame is successfully 153119150Ssam * dispatched to the driver, then it is responsible for freeing the 154119150Ssam * reference (and potentially free'ing up any associated storage). 155119150Ssam */ 156170530Ssamint 157138568Ssamieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, 158170530Ssam struct mbuf *m, int type) 159116742Ssam{ 160138568Ssam struct ifnet *ifp = ic->ic_ifp; 161116742Ssam struct ieee80211_frame *wh; 162116742Ssam 163119150Ssam KASSERT(ni != NULL, ("null node")); 164116742Ssam 165119150Ssam /* 166119150Ssam * Yech, hack alert! We want to pass the node down to the 167119150Ssam * driver's start routine. If we don't do so then the start 168119150Ssam * routine must immediately look it up again and that can 169119150Ssam * cause a lock order reversal if, for example, this frame 170119150Ssam * is being sent because the station is being timedout and 171119150Ssam * the frame being sent is a DEAUTH message. We could stick 172119150Ssam * this in an m_tag and tack that on to the mbuf. However 173119150Ssam * that's rather expensive to do for every frame so instead 174119150Ssam * we stuff it in the rcvif field since outbound frames do 175119150Ssam * not (presently) use this. 176119150Ssam */ 177116742Ssam M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 178116742Ssam if (m == NULL) 179116742Ssam return ENOMEM; 180119150Ssam KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); 181119150Ssam m->m_pkthdr.rcvif = (void *)ni; 182119150Ssam 183116742Ssam wh = mtod(m, struct ieee80211_frame *); 184148314Ssam ieee80211_send_setup(ic, ni, wh, 185148314Ssam IEEE80211_FC0_TYPE_MGT | type, 186148314Ssam ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); 187138568Ssam if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { 188138568Ssam m->m_flags &= ~M_LINK0; 189138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 190138568Ssam "[%s] encrypting frame (%s)\n", 191138568Ssam ether_sprintf(wh->i_addr1), __func__); 192138568Ssam wh->i_fc[1] |= IEEE80211_FC1_WEP; 193138568Ssam } 194172231Ssam if (ni->ni_flags & IEEE80211_NODE_QOS) { 195172231Ssam /* NB: force all management frames to the highest queue */ 196172231Ssam M_WME_SETAC(m, WME_AC_VO); 197172231Ssam } else 198172231Ssam M_WME_SETAC(m, WME_AC_BE); 199116742Ssam#ifdef IEEE80211_DEBUG 200138568Ssam /* avoid printing too many frames */ 201138568Ssam if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || 202138568Ssam ieee80211_msg_dumppkts(ic)) { 203138568Ssam printf("[%s] send %s on channel %u\n", 204138568Ssam ether_sprintf(wh->i_addr1), 205138568Ssam ieee80211_mgt_subtype_name[ 206138568Ssam (type & IEEE80211_FC0_SUBTYPE_MASK) >> 207138568Ssam IEEE80211_FC0_SUBTYPE_SHIFT], 208148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan)); 209138568Ssam } 210116742Ssam#endif 211138568Ssam IEEE80211_NODE_STAT(ni, tx_mgmt); 212116742Ssam IF_ENQUEUE(&ic->ic_mgtq, m); 213132712Srwatson if_start(ifp); 214170530Ssam ifp->if_opackets++; 215170530Ssam 216116742Ssam return 0; 217116742Ssam} 218116742Ssam 219119150Ssam/* 220160690Ssam * Raw packet transmit stub for legacy drivers. 221160690Ssam * Send the packet through the mgt q so we bypass 222160690Ssam * the normal encapsulation work. 223160690Ssam */ 224160690Ssamint 225160690Ssamieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 226160690Ssam const struct ieee80211_bpf_params *params) 227160690Ssam{ 228160690Ssam struct ieee80211com *ic = ni->ni_ic; 229160690Ssam struct ifnet *ifp = ic->ic_ifp; 230160690Ssam 231160690Ssam m->m_pkthdr.rcvif = (void *) ni; 232160690Ssam IF_ENQUEUE(&ic->ic_mgtq, m); 233160690Ssam if_start(ifp); 234160690Ssam ifp->if_opackets++; 235160690Ssam 236160690Ssam return 0; 237160690Ssam} 238160690Ssam 239160690Ssam/* 240160690Ssam * 802.11 output routine. This is (currently) used only to 241160690Ssam * connect bpf write calls to the 802.11 layer for injecting 242160690Ssam * raw 802.11 frames. Note we locate the ieee80211com from 243160690Ssam * the ifnet using a spare field setup at attach time. This 244160690Ssam * will go away when the virtual ap support comes in. 245160690Ssam */ 246160690Ssamint 247160690Ssamieee80211_output(struct ifnet *ifp, struct mbuf *m, 248160690Ssam struct sockaddr *dst, struct rtentry *rt0) 249160690Ssam{ 250160690Ssam#define senderr(e) do { error = (e); goto bad;} while (0) 251160690Ssam struct ieee80211com *ic = ifp->if_spare2; /* XXX */ 252160690Ssam struct ieee80211_node *ni = NULL; 253160690Ssam struct ieee80211_frame *wh; 254160690Ssam int error; 255160690Ssam 256160690Ssam /* 257160690Ssam * Hand to the 802.3 code if not tagged as 258160690Ssam * a raw 802.11 frame. 259160690Ssam */ 260160690Ssam if (dst->sa_family != AF_IEEE80211) 261160690Ssam return ether_output(ifp, m, dst, rt0); 262160690Ssam#ifdef MAC 263160690Ssam error = mac_check_ifnet_transmit(ifp, m); 264160690Ssam if (error) 265160690Ssam senderr(error); 266160690Ssam#endif 267160690Ssam if (ifp->if_flags & IFF_MONITOR) 268160690Ssam senderr(ENETDOWN); 269160690Ssam if ((ifp->if_flags & IFF_UP) == 0) 270160690Ssam senderr(ENETDOWN); 271160690Ssam 272160690Ssam /* XXX bypass bridge, pfil, carp, etc. */ 273160690Ssam 274160690Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) 275160690Ssam senderr(EIO); /* XXX */ 276160690Ssam wh = mtod(m, struct ieee80211_frame *); 277160690Ssam if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 278160690Ssam IEEE80211_FC0_VERSION_0) 279160690Ssam senderr(EIO); /* XXX */ 280160690Ssam 281160690Ssam /* locate destination node */ 282160690Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 283160690Ssam case IEEE80211_FC1_DIR_NODS: 284160690Ssam case IEEE80211_FC1_DIR_FROMDS: 285160690Ssam ni = ieee80211_find_txnode(ic, wh->i_addr1); 286160690Ssam break; 287160690Ssam case IEEE80211_FC1_DIR_TODS: 288160690Ssam case IEEE80211_FC1_DIR_DSTODS: 289160690Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) 290160690Ssam senderr(EIO); /* XXX */ 291160690Ssam ni = ieee80211_find_txnode(ic, wh->i_addr3); 292160690Ssam break; 293160690Ssam default: 294160690Ssam senderr(EIO); /* XXX */ 295160690Ssam } 296160690Ssam if (ni == NULL) { 297160690Ssam /* 298160690Ssam * Permit packets w/ bpf params through regardless 299160690Ssam * (see below about sa_len). 300160690Ssam */ 301160690Ssam if (dst->sa_len == 0) 302160690Ssam senderr(EHOSTUNREACH); 303160690Ssam ni = ieee80211_ref_node(ic->ic_bss); 304160690Ssam } 305160690Ssam 306160690Ssam /* XXX ctrl frames should go through */ 307160690Ssam if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 308160690Ssam (m->m_flags & M_PWR_SAV) == 0) { 309160690Ssam /* 310160690Ssam * Station in power save mode; pass the frame 311160690Ssam * to the 802.11 layer and continue. We'll get 312160690Ssam * the frame back when the time is right. 313160690Ssam */ 314170530Ssam ieee80211_pwrsave(ni, m); 315160690Ssam error = 0; 316160690Ssam goto reclaim; 317160690Ssam } 318160690Ssam 319160690Ssam /* calculate priority so drivers can find the tx queue */ 320160690Ssam /* XXX assumes an 802.3 frame */ 321160690Ssam if (ieee80211_classify(ic, m, ni)) 322160690Ssam senderr(EIO); /* XXX */ 323160690Ssam 324160690Ssam BPF_MTAP(ifp, m); 325160690Ssam /* 326160690Ssam * NB: DLT_IEEE802_11_RADIO identifies the parameters are 327160690Ssam * present by setting the sa_len field of the sockaddr (yes, 328160690Ssam * this is a hack). 329160690Ssam * NB: we assume sa_data is suitably aligned to cast. 330160690Ssam */ 331160690Ssam return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *) 332160690Ssam (dst->sa_len ? dst->sa_data : NULL)); 333160690Ssambad: 334160690Ssam if (m != NULL) 335160690Ssam m_freem(m); 336160690Ssamreclaim: 337160690Ssam if (ni != NULL) 338160690Ssam ieee80211_free_node(ni); 339160690Ssam return error; 340160690Ssam#undef senderr 341160690Ssam} 342160690Ssam 343160690Ssam/* 344138568Ssam * Send a null data frame to the specified node. 345148582Ssam * 346148582Ssam * NB: the caller is assumed to have setup a node reference 347148582Ssam * for use; this is necessary to deal with a race condition 348148582Ssam * when probing for inactive stations. 349119150Ssam */ 350138568Ssamint 351148301Ssamieee80211_send_nulldata(struct ieee80211_node *ni) 352138568Ssam{ 353148301Ssam struct ieee80211com *ic = ni->ni_ic; 354138568Ssam struct ifnet *ifp = ic->ic_ifp; 355138568Ssam struct mbuf *m; 356138568Ssam struct ieee80211_frame *wh; 357138568Ssam 358151967Sandre MGETHDR(m, M_NOWAIT, MT_DATA); 359138568Ssam if (m == NULL) { 360138568Ssam /* XXX debug msg */ 361170530Ssam ieee80211_unref_node(&ni); 362138568Ssam ic->ic_stats.is_tx_nobuf++; 363138568Ssam return ENOMEM; 364138568Ssam } 365170530Ssam MH_ALIGN(m, sizeof(struct ieee80211_frame)); 366148582Ssam m->m_pkthdr.rcvif = (void *) ni; 367138568Ssam 368138568Ssam wh = mtod(m, struct ieee80211_frame *); 369148314Ssam ieee80211_send_setup(ic, ni, wh, 370148314Ssam IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, 371148314Ssam ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); 372148314Ssam /* NB: power management bit is never sent by an AP */ 373148314Ssam if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 374170530Ssam ic->ic_opmode != IEEE80211_M_HOSTAP && 375170530Ssam ic->ic_opmode != IEEE80211_M_WDS) 376148314Ssam wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; 377138568Ssam m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); 378172231Ssam M_WME_SETAC(m, WME_AC_BE); 379138568Ssam 380138568Ssam IEEE80211_NODE_STAT(ni, tx_data); 381138568Ssam 382148314Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 383148314Ssam "[%s] send null data frame on channel %u, pwr mgt %s\n", 384148314Ssam ether_sprintf(ni->ni_macaddr), 385148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 386148314Ssam wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); 387148314Ssam 388138568Ssam IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ 389138568Ssam if_start(ifp); 390138568Ssam 391138568Ssam return 0; 392138568Ssam} 393138568Ssam 394138568Ssam/* 395138568Ssam * Assign priority to a frame based on any vlan tag assigned 396138568Ssam * to the station and/or any Diffserv setting in an IP header. 397138568Ssam * Finally, if an ACM policy is setup (in station mode) it's 398138568Ssam * applied. 399138568Ssam */ 400138568Ssamint 401138568Ssamieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) 402138568Ssam{ 403138568Ssam int v_wme_ac, d_wme_ac, ac; 404138568Ssam#ifdef INET 405138568Ssam struct ether_header *eh; 406138568Ssam#endif 407138568Ssam 408138568Ssam if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { 409138568Ssam ac = WME_AC_BE; 410138568Ssam goto done; 411138568Ssam } 412138568Ssam 413138568Ssam /* 414138568Ssam * If node has a vlan tag then all traffic 415138568Ssam * to it must have a matching tag. 416138568Ssam */ 417138568Ssam v_wme_ac = 0; 418138568Ssam if (ni->ni_vlan != 0) { 419162375Sandre if ((m->m_flags & M_VLANTAG) == 0) { 420138568Ssam IEEE80211_NODE_STAT(ni, tx_novlantag); 421138568Ssam return 1; 422138568Ssam } 423162375Sandre if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != 424138568Ssam EVL_VLANOFTAG(ni->ni_vlan)) { 425138568Ssam IEEE80211_NODE_STAT(ni, tx_vlanmismatch); 426138568Ssam return 1; 427138568Ssam } 428138568Ssam /* map vlan priority to AC */ 429138568Ssam switch (EVL_PRIOFTAG(ni->ni_vlan)) { 430138568Ssam case 1: 431138568Ssam case 2: 432138568Ssam v_wme_ac = WME_AC_BK; 433138568Ssam break; 434138568Ssam case 0: 435138568Ssam case 3: 436138568Ssam v_wme_ac = WME_AC_BE; 437138568Ssam break; 438138568Ssam case 4: 439138568Ssam case 5: 440138568Ssam v_wme_ac = WME_AC_VI; 441138568Ssam break; 442138568Ssam case 6: 443138568Ssam case 7: 444138568Ssam v_wme_ac = WME_AC_VO; 445138568Ssam break; 446138568Ssam } 447138568Ssam } 448138568Ssam 449138568Ssam#ifdef INET 450138568Ssam eh = mtod(m, struct ether_header *); 451138568Ssam if (eh->ether_type == htons(ETHERTYPE_IP)) { 452138568Ssam const struct ip *ip = (struct ip *) 453170530Ssam (mtod(m, uint8_t *) + sizeof (*eh)); 454138568Ssam /* 455138568Ssam * IP frame, map the TOS field. 456138568Ssam */ 457138568Ssam switch (ip->ip_tos) { 458138568Ssam case 0x08: 459138568Ssam case 0x20: 460138568Ssam d_wme_ac = WME_AC_BK; /* background */ 461138568Ssam break; 462138568Ssam case 0x28: 463138568Ssam case 0xa0: 464138568Ssam d_wme_ac = WME_AC_VI; /* video */ 465138568Ssam break; 466138568Ssam case 0x30: /* voice */ 467138568Ssam case 0xe0: 468138568Ssam case 0x88: /* XXX UPSD */ 469138568Ssam case 0xb8: 470138568Ssam d_wme_ac = WME_AC_VO; 471138568Ssam break; 472138568Ssam default: 473138568Ssam d_wme_ac = WME_AC_BE; 474138568Ssam break; 475138568Ssam } 476138568Ssam } else { 477138568Ssam#endif /* INET */ 478138568Ssam d_wme_ac = WME_AC_BE; 479138568Ssam#ifdef INET 480138568Ssam } 481138568Ssam#endif 482138568Ssam /* 483138568Ssam * Use highest priority AC. 484138568Ssam */ 485138568Ssam if (v_wme_ac > d_wme_ac) 486138568Ssam ac = v_wme_ac; 487138568Ssam else 488138568Ssam ac = d_wme_ac; 489138568Ssam 490138568Ssam /* 491138568Ssam * Apply ACM policy. 492138568Ssam */ 493138568Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 494138568Ssam static const int acmap[4] = { 495138568Ssam WME_AC_BK, /* WME_AC_BE */ 496138568Ssam WME_AC_BK, /* WME_AC_BK */ 497138568Ssam WME_AC_BE, /* WME_AC_VI */ 498138568Ssam WME_AC_VI, /* WME_AC_VO */ 499138568Ssam }; 500138568Ssam while (ac != WME_AC_BK && 501138568Ssam ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) 502138568Ssam ac = acmap[ac]; 503138568Ssam } 504138568Ssamdone: 505138568Ssam M_WME_SETAC(m, ac); 506138568Ssam return 0; 507138568Ssam} 508138568Ssam 509138568Ssam/* 510139527Ssam * Insure there is sufficient contiguous space to encapsulate the 511139527Ssam * 802.11 data frame. If room isn't already there, arrange for it. 512139527Ssam * Drivers and cipher modules assume we have done the necessary work 513139527Ssam * and fail rudely if they don't find the space they need. 514139527Ssam */ 515139527Ssamstatic struct mbuf * 516139527Ssamieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, 517139527Ssam struct ieee80211_key *key, struct mbuf *m) 518139527Ssam{ 519164805Ssam#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) 520170530Ssam int needed_space = ic->ic_headroom + hdrsize; 521139527Ssam 522139527Ssam if (key != NULL) { 523139527Ssam /* XXX belongs in crypto code? */ 524139527Ssam needed_space += key->wk_cipher->ic_header; 525139527Ssam /* XXX frags */ 526156758Ssam /* 527156758Ssam * When crypto is being done in the host we must insure 528156758Ssam * the data are writable for the cipher routines; clone 529156758Ssam * a writable mbuf chain. 530156758Ssam * XXX handle SWMIC specially 531156758Ssam */ 532156758Ssam if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) { 533156758Ssam m = m_unshare(m, M_NOWAIT); 534156758Ssam if (m == NULL) { 535156758Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, 536156758Ssam "%s: cannot get writable mbuf\n", __func__); 537156758Ssam ic->ic_stats.is_tx_nobuf++; /* XXX new stat */ 538156758Ssam return NULL; 539156758Ssam } 540156758Ssam } 541139527Ssam } 542139527Ssam /* 543139527Ssam * We know we are called just before stripping an Ethernet 544139527Ssam * header and prepending an LLC header. This means we know 545139527Ssam * there will be 546164805Ssam * sizeof(struct ether_header) - sizeof(struct llc) 547139527Ssam * bytes recovered to which we need additional space for the 548139527Ssam * 802.11 header and any crypto header. 549139527Ssam */ 550139527Ssam /* XXX check trailing space and copy instead? */ 551139527Ssam if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { 552139527Ssam struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); 553139527Ssam if (n == NULL) { 554139527Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, 555139527Ssam "%s: cannot expand storage\n", __func__); 556139527Ssam ic->ic_stats.is_tx_nobuf++; 557139527Ssam m_freem(m); 558139527Ssam return NULL; 559139527Ssam } 560139527Ssam KASSERT(needed_space <= MHLEN, 561139527Ssam ("not enough room, need %u got %zu\n", needed_space, MHLEN)); 562139527Ssam /* 563139527Ssam * Setup new mbuf to have leading space to prepend the 564139527Ssam * 802.11 header and any crypto header bits that are 565139527Ssam * required (the latter are added when the driver calls 566139527Ssam * back to ieee80211_crypto_encap to do crypto encapsulation). 567139527Ssam */ 568139527Ssam /* NB: must be first 'cuz it clobbers m_data */ 569139527Ssam m_move_pkthdr(n, m); 570139527Ssam n->m_len = 0; /* NB: m_gethdr does not set */ 571139527Ssam n->m_data += needed_space; 572139527Ssam /* 573139527Ssam * Pull up Ethernet header to create the expected layout. 574139527Ssam * We could use m_pullup but that's overkill (i.e. we don't 575139527Ssam * need the actual data) and it cannot fail so do it inline 576139527Ssam * for speed. 577139527Ssam */ 578139527Ssam /* NB: struct ether_header is known to be contiguous */ 579139527Ssam n->m_len += sizeof(struct ether_header); 580139527Ssam m->m_len -= sizeof(struct ether_header); 581139527Ssam m->m_data += sizeof(struct ether_header); 582139527Ssam /* 583139527Ssam * Replace the head of the chain. 584139527Ssam */ 585139527Ssam n->m_next = m; 586139527Ssam m = n; 587139527Ssam } 588139527Ssam return m; 589139527Ssam#undef TO_BE_RECLAIMED 590139527Ssam} 591139527Ssam 592139527Ssam/* 593139527Ssam * Return the transmit key to use in sending a unicast frame. 594139527Ssam * If a unicast key is set we use that. When no unicast key is set 595139527Ssam * we fall back to the default transmit key. 596138568Ssam */ 597138568Ssamstatic __inline struct ieee80211_key * 598139527Ssamieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni) 599138568Ssam{ 600167432Ssam if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { 601138568Ssam if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || 602167432Ssam IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey])) 603138568Ssam return NULL; 604138568Ssam return &ic->ic_nw_keys[ic->ic_def_txkey]; 605138568Ssam } else { 606138568Ssam return &ni->ni_ucastkey; 607138568Ssam } 608138568Ssam} 609138568Ssam 610138568Ssam/* 611139527Ssam * Return the transmit key to use in sending a multicast frame. 612139527Ssam * Multicast traffic always uses the group key which is installed as 613139527Ssam * the default tx key. 614139527Ssam */ 615139527Ssamstatic __inline struct ieee80211_key * 616139527Ssamieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni) 617139527Ssam{ 618139527Ssam if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || 619167432Ssam IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey])) 620139527Ssam return NULL; 621139527Ssam return &ic->ic_nw_keys[ic->ic_def_txkey]; 622139527Ssam} 623139527Ssam 624139527Ssam/* 625138568Ssam * Encapsulate an outbound data frame. The mbuf chain is updated. 626138568Ssam * If an error is encountered NULL is returned. The caller is required 627138568Ssam * to provide a node reference and pullup the ethernet header in the 628138568Ssam * first mbuf. 629138568Ssam */ 630116742Ssamstruct mbuf * 631138568Ssamieee80211_encap(struct ieee80211com *ic, struct mbuf *m, 632138568Ssam struct ieee80211_node *ni) 633116742Ssam{ 634116742Ssam struct ether_header eh; 635116742Ssam struct ieee80211_frame *wh; 636138568Ssam struct ieee80211_key *key; 637116742Ssam struct llc *llc; 638170530Ssam int hdrsize, datalen, addqos, txfrag, isff; 639116742Ssam 640170530Ssam /* 641170530Ssam * Copy existing Ethernet header to a safe place. The 642170530Ssam * rest of the code assumes it's ok to strip it when 643170530Ssam * reorganizing state for the final encapsulation. 644170530Ssam */ 645138568Ssam KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); 646116742Ssam memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); 647116742Ssam 648138568Ssam /* 649138568Ssam * Insure space for additional headers. First identify 650138568Ssam * transmit key to use in calculating any buffer adjustments 651138568Ssam * required. This is also used below to do privacy 652138568Ssam * encapsulation work. Then calculate the 802.11 header 653138568Ssam * size and any padding required by the driver. 654138568Ssam * 655138568Ssam * Note key may be NULL if we fall back to the default 656138568Ssam * transmit key and that is not set. In that case the 657138568Ssam * buffer may not be expanded as needed by the cipher 658138568Ssam * routines, but they will/should discard it. 659138568Ssam */ 660138568Ssam if (ic->ic_flags & IEEE80211_F_PRIVACY) { 661139527Ssam if (ic->ic_opmode == IEEE80211_M_STA || 662139527Ssam !IEEE80211_IS_MULTICAST(eh.ether_dhost)) 663139527Ssam key = ieee80211_crypto_getucastkey(ic, ni); 664139527Ssam else 665139527Ssam key = ieee80211_crypto_getmcastkey(ic, ni); 666138568Ssam if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) { 667138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, 668139527Ssam "[%s] no default transmit key (%s) deftxkey %u\n", 669139527Ssam ether_sprintf(eh.ether_dhost), __func__, 670139527Ssam ic->ic_def_txkey); 671139527Ssam ic->ic_stats.is_tx_nodefkey++; 672171950Ssam goto bad; 673138568Ssam } 674138568Ssam } else 675138568Ssam key = NULL; 676138568Ssam /* XXX 4-address format */ 677139527Ssam /* 678139527Ssam * XXX Some ap's don't handle QoS-encapsulated EAPOL 679139527Ssam * frames so suppress use. This may be an issue if other 680139527Ssam * ap's require all data frames to be QoS-encapsulated 681139527Ssam * once negotiated in which case we'll need to make this 682139527Ssam * configurable. 683139527Ssam */ 684170530Ssam addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) && 685139527Ssam eh.ether_type != htons(ETHERTYPE_PAE); 686139527Ssam if (addqos) 687138568Ssam hdrsize = sizeof(struct ieee80211_qosframe); 688138568Ssam else 689138568Ssam hdrsize = sizeof(struct ieee80211_frame); 690138568Ssam if (ic->ic_flags & IEEE80211_F_DATAPAD) 691170530Ssam hdrsize = roundup(hdrsize, sizeof(uint32_t)); 692170530Ssam 693170530Ssam if ((isff = m->m_flags & M_FF) != 0) { 694170530Ssam struct mbuf *m2; 695170530Ssam struct ether_header eh2; 696170530Ssam 697170530Ssam /* 698170530Ssam * Fast frame encapsulation. There must be two packets 699170530Ssam * chained with m_nextpkt. We do header adjustment for 700170530Ssam * each, add the tunnel encapsulation, and then concatenate 701170530Ssam * the mbuf chains to form a single frame for transmission. 702170530Ssam */ 703170530Ssam m2 = m->m_nextpkt; 704170530Ssam if (m2 == NULL) { 705170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 706170530Ssam "%s: only one frame\n", __func__); 707170530Ssam goto bad; 708170530Ssam } 709170530Ssam m->m_nextpkt = NULL; 710170530Ssam /* 711170530Ssam * Include fast frame headers in adjusting header 712170530Ssam * layout; this allocates space according to what 713170530Ssam * ieee80211_encap_fastframe will do. 714170530Ssam */ 715170530Ssam m = ieee80211_mbuf_adjust(ic, 716170530Ssam hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 + 717170530Ssam sizeof(struct ether_header), 718170530Ssam key, m); 719170530Ssam if (m == NULL) { 720170530Ssam /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ 721170530Ssam m_freem(m2); 722170530Ssam goto bad; 723170530Ssam } 724170530Ssam /* 725170530Ssam * Copy second frame's Ethernet header out of line 726170530Ssam * and adjust for encapsulation headers. Note that 727170530Ssam * we make room for padding in case there isn't room 728170530Ssam * at the end of first frame. 729170530Ssam */ 730170530Ssam KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); 731170530Ssam memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header)); 732170530Ssam m2 = ieee80211_mbuf_adjust(ic, 733170530Ssam ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header), 734170530Ssam NULL, m2); 735170530Ssam if (m2 == NULL) { 736170530Ssam /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ 737170530Ssam goto bad; 738170530Ssam } 739170530Ssam m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2); 740170530Ssam if (m == NULL) 741170530Ssam goto bad; 742170530Ssam } else { 743170530Ssam /* 744170530Ssam * Normal frame. 745170530Ssam */ 746170530Ssam m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); 747170530Ssam if (m == NULL) { 748170530Ssam /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ 749170530Ssam goto bad; 750170530Ssam } 751170530Ssam /* NB: this could be optimized 'cuz of ieee80211_mbuf_adjust */ 752170530Ssam m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 753170530Ssam llc = mtod(m, struct llc *); 754170530Ssam llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 755170530Ssam llc->llc_control = LLC_UI; 756170530Ssam llc->llc_snap.org_code[0] = 0; 757170530Ssam llc->llc_snap.org_code[1] = 0; 758170530Ssam llc->llc_snap.org_code[2] = 0; 759170530Ssam llc->llc_snap.ether_type = eh.ether_type; 760127772Ssam } 761138568Ssam datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ 762138568Ssam 763138568Ssam M_PREPEND(m, hdrsize, M_DONTWAIT); 764121180Ssam if (m == NULL) { 765138568Ssam ic->ic_stats.is_tx_nobuf++; 766119150Ssam goto bad; 767121180Ssam } 768116742Ssam wh = mtod(m, struct ieee80211_frame *); 769116742Ssam wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 770170530Ssam *(uint16_t *)wh->i_dur = 0; 771116742Ssam switch (ic->ic_opmode) { 772116742Ssam case IEEE80211_M_STA: 773116742Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 774116742Ssam IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); 775116742Ssam IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 776116742Ssam IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 777116742Ssam break; 778116742Ssam case IEEE80211_M_IBSS: 779116742Ssam case IEEE80211_M_AHDEMO: 780116742Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 781116742Ssam IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 782116742Ssam IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 783140636Ssam /* 784140636Ssam * NB: always use the bssid from ic_bss as the 785140636Ssam * neighbor's may be stale after an ibss merge 786140636Ssam */ 787140636Ssam IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid); 788116742Ssam break; 789116742Ssam case IEEE80211_M_HOSTAP: 790116742Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 791116742Ssam IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 792116742Ssam IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); 793116742Ssam IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 794116742Ssam break; 795117817Ssam case IEEE80211_M_MONITOR: 796170530Ssam case IEEE80211_M_WDS: 797119150Ssam goto bad; 798116742Ssam } 799147789Ssam if (m->m_flags & M_MORE_DATA) 800147789Ssam wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 801139527Ssam if (addqos) { 802138568Ssam struct ieee80211_qosframe *qwh = 803138568Ssam (struct ieee80211_qosframe *) wh; 804138568Ssam int ac, tid; 805138568Ssam 806138568Ssam ac = M_WME_GETAC(m); 807138568Ssam /* map from access class/queue to 11e header priorty value */ 808138568Ssam tid = WME_AC_TO_TID(ac); 809138568Ssam qwh->i_qos[0] = tid & IEEE80211_QOS_TID; 810170530Ssam /* 811170530Ssam * Check if A-MPDU tx aggregation is setup or if we 812170530Ssam * should try to enable it. The sta must be associated 813170530Ssam * with HT and A-MPDU enabled for use. On the first 814170530Ssam * frame that goes out We issue an ADDBA request and 815170530Ssam * wait for a reply. The frame being encapsulated 816170530Ssam * will go out w/o using A-MPDU, or possibly it might 817170530Ssam * be collected by the driver and held/retransmit. 818170530Ssam * ieee80211_ampdu_request handles staggering requests 819170530Ssam * in case the receiver NAK's us or we are otherwise 820170530Ssam * unable to establish a BA stream. 821170530Ssam */ 822170530Ssam if ((ni->ni_flags & IEEE80211_NODE_HT) && 823170530Ssam (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) { 824170530Ssam struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; 825170530Ssam 826170530Ssam if (IEEE80211_AMPDU_RUNNING(tap)) { 827170530Ssam /* 828170530Ssam * Operational, mark frame for aggregation. 829170530Ssam */ 830170530Ssam qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA; 831170530Ssam } else if (!IEEE80211_AMPDU_REQUESTED(tap)) { 832170530Ssam /* 833170530Ssam * Not negotiated yet, request service. 834170530Ssam */ 835170530Ssam ieee80211_ampdu_request(ni, tap); 836170530Ssam } 837170530Ssam } 838170530Ssam /* XXX works even when BA marked above */ 839138568Ssam if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) 840170530Ssam qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; 841138568Ssam qwh->i_qos[1] = 0; 842138568Ssam qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; 843138568Ssam 844170530Ssam *(uint16_t *)wh->i_seq = 845138568Ssam htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); 846138568Ssam ni->ni_txseqs[tid]++; 847138568Ssam } else { 848170530Ssam *(uint16_t *)wh->i_seq = 849167439Ssam htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT); 850167439Ssam ni->ni_txseqs[IEEE80211_NONQOS_TID]++; 851138568Ssam } 852170530Ssam /* check if xmit fragmentation is required */ 853170530Ssam txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold && 854170530Ssam !IEEE80211_IS_MULTICAST(wh->i_addr1) && 855170530Ssam !isff); /* NB: don't fragment ff's */ 856139527Ssam if (key != NULL) { 857139527Ssam /* 858139527Ssam * IEEE 802.1X: send EAPOL frames always in the clear. 859139527Ssam * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. 860139527Ssam */ 861139527Ssam if (eh.ether_type != htons(ETHERTYPE_PAE) || 862139527Ssam ((ic->ic_flags & IEEE80211_F_WPA) && 863141660Ssam (ic->ic_opmode == IEEE80211_M_STA ? 864167432Ssam !IEEE80211_KEY_UNDEFINED(key) : 865167432Ssam !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { 866139527Ssam wh->i_fc[1] |= IEEE80211_FC1_WEP; 867170530Ssam if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) { 868139527Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, 869139527Ssam "[%s] enmic failed, discard frame\n", 870139527Ssam ether_sprintf(eh.ether_dhost)); 871139527Ssam ic->ic_stats.is_crypto_enmicfail++; 872139527Ssam goto bad; 873139527Ssam } 874139527Ssam } 875139527Ssam } 876170530Ssam /* 877170530Ssam * NB: frag flags may leak from above; they should only 878170530Ssam * be set on return to the caller if we fragment at 879170530Ssam * the 802.11 layer. 880170530Ssam */ 881170530Ssam m->m_flags &= ~(M_FRAG | M_FIRSTFRAG); 882170530Ssam if (txfrag && !ieee80211_fragment(ic, m, hdrsize, 883170530Ssam key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold)) 884170530Ssam goto bad; 885138568Ssam 886138568Ssam IEEE80211_NODE_STAT(ni, tx_data); 887161144Ssam if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 888161144Ssam IEEE80211_NODE_STAT(ni, tx_mcast); 889161144Ssam else 890161144Ssam IEEE80211_NODE_STAT(ni, tx_ucast); 891138568Ssam IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); 892138568Ssam 893116742Ssam return m; 894119150Ssambad: 895119150Ssam if (m != NULL) 896119150Ssam m_freem(m); 897119150Ssam return NULL; 898116742Ssam} 899116742Ssam 900116742Ssam/* 901170530Ssam * Do Ethernet-LLC encapsulation for each payload in a fast frame 902170530Ssam * tunnel encapsulation. The frame is assumed to have an Ethernet 903170530Ssam * header at the front that must be stripped before prepending the 904170530Ssam * LLC followed by the Ethernet header passed in (with an Ethernet 905170530Ssam * type that specifies the payload size). 906170530Ssam */ 907170530Ssamstatic struct mbuf * 908170530Ssamieee80211_encap1(struct ieee80211com *ic, struct mbuf *m, 909170530Ssam const struct ether_header *eh) 910170530Ssam{ 911170530Ssam struct llc *llc; 912170530Ssam uint16_t payload; 913170530Ssam 914170530Ssam /* XXX optimize by combining m_adj+M_PREPEND */ 915170530Ssam m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 916170530Ssam llc = mtod(m, struct llc *); 917170530Ssam llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 918170530Ssam llc->llc_control = LLC_UI; 919170530Ssam llc->llc_snap.org_code[0] = 0; 920170530Ssam llc->llc_snap.org_code[1] = 0; 921170530Ssam llc->llc_snap.org_code[2] = 0; 922170530Ssam llc->llc_snap.ether_type = eh->ether_type; 923170530Ssam payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */ 924170530Ssam 925170530Ssam M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT); 926170530Ssam if (m == NULL) { /* XXX cannot happen */ 927170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 928170530Ssam "%s: no space for ether_header\n", __func__); 929170530Ssam ic->ic_stats.is_tx_nobuf++; 930170530Ssam return NULL; 931170530Ssam } 932170530Ssam ETHER_HEADER_COPY(mtod(m, void *), eh); 933170530Ssam mtod(m, struct ether_header *)->ether_type = htons(payload); 934170530Ssam return m; 935170530Ssam} 936170530Ssam 937170530Ssam/* 938170530Ssam * Do fast frame tunnel encapsulation. The two frames and 939170530Ssam * Ethernet headers are supplied. The caller is assumed to 940170530Ssam * have arrange for space in the mbuf chains for encapsulating 941170530Ssam * headers (to avoid major mbuf fragmentation). 942170530Ssam * 943170530Ssam * The encapsulated frame is returned or NULL if there is a 944170530Ssam * problem (should not happen). 945170530Ssam */ 946170530Ssamstatic struct mbuf * 947170530Ssamieee80211_encap_fastframe(struct ieee80211com *ic, 948170530Ssam struct mbuf *m1, const struct ether_header *eh1, 949170530Ssam struct mbuf *m2, const struct ether_header *eh2) 950170530Ssam{ 951170530Ssam struct llc *llc; 952170530Ssam struct mbuf *m; 953170530Ssam int pad; 954170530Ssam 955170530Ssam /* 956170530Ssam * First, each frame gets a standard encapsulation. 957170530Ssam */ 958170530Ssam m1 = ieee80211_encap1(ic, m1, eh1); 959170530Ssam if (m1 == NULL) { 960170530Ssam m_freem(m2); 961170530Ssam return NULL; 962170530Ssam } 963170530Ssam m2 = ieee80211_encap1(ic, m2, eh2); 964170530Ssam if (m2 == NULL) { 965170530Ssam m_freem(m1); 966170530Ssam return NULL; 967170530Ssam } 968170530Ssam 969170530Ssam /* 970170530Ssam * Pad leading frame to a 4-byte boundary. If there 971170530Ssam * is space at the end of the first frame, put it 972170530Ssam * there; otherwise prepend to the front of the second 973170530Ssam * frame. We know doing the second will always work 974170530Ssam * because we reserve space above. We prefer appending 975170530Ssam * as this typically has better DMA alignment properties. 976170530Ssam */ 977170530Ssam for (m = m1; m->m_next != NULL; m = m->m_next) 978170530Ssam ; 979170530Ssam pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len; 980170530Ssam if (pad) { 981170530Ssam if (M_TRAILINGSPACE(m) < pad) { /* prepend to second */ 982170530Ssam m2->m_data -= pad; 983170530Ssam m2->m_len += pad; 984170530Ssam m2->m_pkthdr.len += pad; 985170530Ssam } else { /* append to first */ 986170530Ssam m->m_len += pad; 987170530Ssam m1->m_pkthdr.len += pad; 988170530Ssam } 989170530Ssam } 990170530Ssam 991170530Ssam /* 992170530Ssam * Now, stick 'em together and prepend the tunnel headers; 993170530Ssam * first the Atheros tunnel header (all zero for now) and 994170530Ssam * then a special fast frame LLC. 995170530Ssam * 996170530Ssam * XXX optimize by prepending together 997170530Ssam */ 998170530Ssam m->m_next = m2; /* NB: last mbuf from above */ 999170530Ssam m1->m_pkthdr.len += m2->m_pkthdr.len; 1000170530Ssam M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT); 1001170530Ssam if (m1 == NULL) { /* XXX cannot happen */ 1002170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 1003170530Ssam "%s: no space for tunnel header\n", __func__); 1004170530Ssam ic->ic_stats.is_tx_nobuf++; 1005170530Ssam return NULL; 1006170530Ssam } 1007170530Ssam memset(mtod(m1, void *), 0, sizeof(uint32_t)+2); 1008170530Ssam 1009170530Ssam M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT); 1010170530Ssam if (m1 == NULL) { /* XXX cannot happen */ 1011170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 1012170530Ssam "%s: no space for llc header\n", __func__); 1013170530Ssam ic->ic_stats.is_tx_nobuf++; 1014170530Ssam return NULL; 1015170530Ssam } 1016170530Ssam llc = mtod(m1, struct llc *); 1017170530Ssam llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 1018170530Ssam llc->llc_control = LLC_UI; 1019170530Ssam llc->llc_snap.org_code[0] = ATH_FF_SNAP_ORGCODE_0; 1020170530Ssam llc->llc_snap.org_code[1] = ATH_FF_SNAP_ORGCODE_1; 1021170530Ssam llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2; 1022170530Ssam llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE); 1023170530Ssam 1024170530Ssam ic->ic_stats.is_ff_encap++; 1025170530Ssam 1026170530Ssam return m1; 1027170530Ssam} 1028170530Ssam 1029170530Ssam/* 1030170530Ssam * Fragment the frame according to the specified mtu. 1031170530Ssam * The size of the 802.11 header (w/o padding) is provided 1032170530Ssam * so we don't need to recalculate it. We create a new 1033170530Ssam * mbuf for each fragment and chain it through m_nextpkt; 1034170530Ssam * we might be able to optimize this by reusing the original 1035170530Ssam * packet's mbufs but that is significantly more complicated. 1036170530Ssam */ 1037170530Ssamstatic int 1038170530Ssamieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0, 1039170530Ssam u_int hdrsize, u_int ciphdrsize, u_int mtu) 1040170530Ssam{ 1041170530Ssam struct ieee80211_frame *wh, *whf; 1042170530Ssam struct mbuf *m, *prev, *next; 1043170530Ssam u_int totalhdrsize, fragno, fragsize, off, remainder, payload; 1044170530Ssam 1045170530Ssam KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?")); 1046170530Ssam KASSERT(m0->m_pkthdr.len > mtu, 1047170530Ssam ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu)); 1048170530Ssam 1049170530Ssam wh = mtod(m0, struct ieee80211_frame *); 1050170530Ssam /* NB: mark the first frag; it will be propagated below */ 1051170530Ssam wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG; 1052170530Ssam totalhdrsize = hdrsize + ciphdrsize; 1053170530Ssam fragno = 1; 1054170530Ssam off = mtu - ciphdrsize; 1055170530Ssam remainder = m0->m_pkthdr.len - off; 1056170530Ssam prev = m0; 1057170530Ssam do { 1058170530Ssam fragsize = totalhdrsize + remainder; 1059170530Ssam if (fragsize > mtu) 1060170530Ssam fragsize = mtu; 1061170530Ssam KASSERT(fragsize < MCLBYTES, 1062170530Ssam ("fragment size %u too big!", fragsize)); 1063170530Ssam if (fragsize > MHLEN) 1064170530Ssam m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1065170530Ssam else 1066170530Ssam m = m_gethdr(M_DONTWAIT, MT_DATA); 1067170530Ssam if (m == NULL) 1068170530Ssam goto bad; 1069170530Ssam /* leave room to prepend any cipher header */ 1070170530Ssam m_align(m, fragsize - ciphdrsize); 1071170530Ssam 1072170530Ssam /* 1073170530Ssam * Form the header in the fragment. Note that since 1074170530Ssam * we mark the first fragment with the MORE_FRAG bit 1075170530Ssam * it automatically is propagated to each fragment; we 1076170530Ssam * need only clear it on the last fragment (done below). 1077170530Ssam */ 1078170530Ssam whf = mtod(m, struct ieee80211_frame *); 1079170530Ssam memcpy(whf, wh, hdrsize); 1080170530Ssam *(uint16_t *)&whf->i_seq[0] |= htole16( 1081170530Ssam (fragno & IEEE80211_SEQ_FRAG_MASK) << 1082170530Ssam IEEE80211_SEQ_FRAG_SHIFT); 1083170530Ssam fragno++; 1084170530Ssam 1085170530Ssam payload = fragsize - totalhdrsize; 1086170530Ssam /* NB: destination is known to be contiguous */ 1087170530Ssam m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize); 1088170530Ssam m->m_len = hdrsize + payload; 1089170530Ssam m->m_pkthdr.len = hdrsize + payload; 1090170530Ssam m->m_flags |= M_FRAG; 1091170530Ssam 1092170530Ssam /* chain up the fragment */ 1093170530Ssam prev->m_nextpkt = m; 1094170530Ssam prev = m; 1095170530Ssam 1096170530Ssam /* deduct fragment just formed */ 1097170530Ssam remainder -= payload; 1098170530Ssam off += payload; 1099170530Ssam } while (remainder != 0); 1100170530Ssam whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG; 1101170530Ssam 1102170530Ssam /* strip first mbuf now that everything has been copied */ 1103170530Ssam m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); 1104170530Ssam m0->m_flags |= M_FIRSTFRAG | M_FRAG; 1105170530Ssam 1106170530Ssam ic->ic_stats.is_tx_fragframes++; 1107170530Ssam ic->ic_stats.is_tx_frags += fragno-1; 1108170530Ssam 1109170530Ssam return 1; 1110170530Ssambad: 1111170530Ssam /* reclaim fragments but leave original frame for caller to free */ 1112170530Ssam for (m = m0->m_nextpkt; m != NULL; m = next) { 1113170530Ssam next = m->m_nextpkt; 1114170530Ssam m->m_nextpkt = NULL; /* XXX paranoid */ 1115170530Ssam m_freem(m); 1116170530Ssam } 1117170530Ssam m0->m_nextpkt = NULL; 1118170530Ssam return 0; 1119170530Ssam} 1120170530Ssam 1121170530Ssam/* 1122116742Ssam * Add a supported rates element id to a frame. 1123116742Ssam */ 1124170530Ssamstatic uint8_t * 1125170530Ssamieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) 1126116742Ssam{ 1127116742Ssam int nrates; 1128116742Ssam 1129116742Ssam *frm++ = IEEE80211_ELEMID_RATES; 1130116742Ssam nrates = rs->rs_nrates; 1131116742Ssam if (nrates > IEEE80211_RATE_SIZE) 1132116742Ssam nrates = IEEE80211_RATE_SIZE; 1133116742Ssam *frm++ = nrates; 1134116742Ssam memcpy(frm, rs->rs_rates, nrates); 1135116742Ssam return frm + nrates; 1136116742Ssam} 1137116742Ssam 1138116742Ssam/* 1139116742Ssam * Add an extended supported rates element id to a frame. 1140116742Ssam */ 1141170530Ssamstatic uint8_t * 1142170530Ssamieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) 1143116742Ssam{ 1144116742Ssam /* 1145116742Ssam * Add an extended supported rates element if operating in 11g mode. 1146116742Ssam */ 1147116742Ssam if (rs->rs_nrates > IEEE80211_RATE_SIZE) { 1148116742Ssam int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 1149116742Ssam *frm++ = IEEE80211_ELEMID_XRATES; 1150116742Ssam *frm++ = nrates; 1151116742Ssam memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 1152116742Ssam frm += nrates; 1153116742Ssam } 1154116742Ssam return frm; 1155116742Ssam} 1156116742Ssam 1157116742Ssam/* 1158116742Ssam * Add an ssid elemet to a frame. 1159116742Ssam */ 1160170530Ssamstatic uint8_t * 1161170530Ssamieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) 1162116742Ssam{ 1163116742Ssam *frm++ = IEEE80211_ELEMID_SSID; 1164116742Ssam *frm++ = len; 1165116742Ssam memcpy(frm, ssid, len); 1166116742Ssam return frm + len; 1167116742Ssam} 1168116742Ssam 1169138568Ssam/* 1170138568Ssam * Add an erp element to a frame. 1171138568Ssam */ 1172170530Ssamstatic uint8_t * 1173170530Ssamieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) 1174116742Ssam{ 1175170530Ssam uint8_t erp; 1176116742Ssam 1177138568Ssam *frm++ = IEEE80211_ELEMID_ERP; 1178138568Ssam *frm++ = 1; 1179138568Ssam erp = 0; 1180138568Ssam if (ic->ic_nonerpsta != 0) 1181138568Ssam erp |= IEEE80211_ERP_NON_ERP_PRESENT; 1182138568Ssam if (ic->ic_flags & IEEE80211_F_USEPROT) 1183138568Ssam erp |= IEEE80211_ERP_USE_PROTECTION; 1184138568Ssam if (ic->ic_flags & IEEE80211_F_USEBARKER) 1185138568Ssam erp |= IEEE80211_ERP_LONG_PREAMBLE; 1186138568Ssam *frm++ = erp; 1187138568Ssam return frm; 1188138568Ssam} 1189138568Ssam 1190170530Ssamstatic uint8_t * 1191170530Ssamieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie) 1192138568Ssam{ 1193138568Ssam#define WPA_OUI_BYTES 0x00, 0x50, 0xf2 1194138568Ssam#define ADDSHORT(frm, v) do { \ 1195138568Ssam frm[0] = (v) & 0xff; \ 1196138568Ssam frm[1] = (v) >> 8; \ 1197138568Ssam frm += 2; \ 1198138568Ssam} while (0) 1199138568Ssam#define ADDSELECTOR(frm, sel) do { \ 1200138568Ssam memcpy(frm, sel, 4); \ 1201138568Ssam frm += 4; \ 1202138568Ssam} while (0) 1203170530Ssam static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; 1204170530Ssam static const uint8_t cipher_suite[][4] = { 1205138568Ssam { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */ 1206138568Ssam { WPA_OUI_BYTES, WPA_CSE_TKIP }, 1207138568Ssam { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */ 1208138568Ssam { WPA_OUI_BYTES, WPA_CSE_CCMP }, 1209138568Ssam { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ 1210138568Ssam { WPA_OUI_BYTES, WPA_CSE_NULL }, 1211138568Ssam }; 1212170530Ssam static const uint8_t wep104_suite[4] = 1213138568Ssam { WPA_OUI_BYTES, WPA_CSE_WEP104 }; 1214170530Ssam static const uint8_t key_mgt_unspec[4] = 1215138568Ssam { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC }; 1216170530Ssam static const uint8_t key_mgt_psk[4] = 1217138568Ssam { WPA_OUI_BYTES, WPA_ASE_8021X_PSK }; 1218138568Ssam const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 1219170530Ssam uint8_t *frm = ie; 1220170530Ssam uint8_t *selcnt; 1221138568Ssam 1222138568Ssam *frm++ = IEEE80211_ELEMID_VENDOR; 1223138568Ssam *frm++ = 0; /* length filled in below */ 1224138568Ssam memcpy(frm, oui, sizeof(oui)); /* WPA OUI */ 1225138568Ssam frm += sizeof(oui); 1226138568Ssam ADDSHORT(frm, WPA_VERSION); 1227138568Ssam 1228138568Ssam /* XXX filter out CKIP */ 1229138568Ssam 1230138568Ssam /* multicast cipher */ 1231138568Ssam if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && 1232138568Ssam rsn->rsn_mcastkeylen >= 13) 1233138568Ssam ADDSELECTOR(frm, wep104_suite); 1234116742Ssam else 1235138568Ssam ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); 1236138568Ssam 1237138568Ssam /* unicast cipher list */ 1238138568Ssam selcnt = frm; 1239138568Ssam ADDSHORT(frm, 0); /* selector count */ 1240138568Ssam if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) { 1241138568Ssam selcnt[0]++; 1242138568Ssam ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); 1243138568Ssam } 1244138568Ssam if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { 1245138568Ssam selcnt[0]++; 1246138568Ssam ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); 1247138568Ssam } 1248138568Ssam 1249138568Ssam /* authenticator selector list */ 1250138568Ssam selcnt = frm; 1251138568Ssam ADDSHORT(frm, 0); /* selector count */ 1252138568Ssam if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { 1253138568Ssam selcnt[0]++; 1254138568Ssam ADDSELECTOR(frm, key_mgt_unspec); 1255138568Ssam } 1256138568Ssam if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { 1257138568Ssam selcnt[0]++; 1258138568Ssam ADDSELECTOR(frm, key_mgt_psk); 1259138568Ssam } 1260138568Ssam 1261138568Ssam /* optional capabilities */ 1262147066Ssam if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH) 1263138568Ssam ADDSHORT(frm, rsn->rsn_caps); 1264138568Ssam 1265138568Ssam /* calculate element length */ 1266138568Ssam ie[1] = frm - ie - 2; 1267138568Ssam KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), 1268138609Ssam ("WPA IE too big, %u > %zu", 1269138568Ssam ie[1]+2, sizeof(struct ieee80211_ie_wpa))); 1270138568Ssam return frm; 1271138568Ssam#undef ADDSHORT 1272138568Ssam#undef ADDSELECTOR 1273138568Ssam#undef WPA_OUI_BYTES 1274116742Ssam} 1275116742Ssam 1276170530Ssamstatic uint8_t * 1277170530Ssamieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie) 1278138568Ssam{ 1279138568Ssam#define RSN_OUI_BYTES 0x00, 0x0f, 0xac 1280138568Ssam#define ADDSHORT(frm, v) do { \ 1281138568Ssam frm[0] = (v) & 0xff; \ 1282138568Ssam frm[1] = (v) >> 8; \ 1283138568Ssam frm += 2; \ 1284138568Ssam} while (0) 1285138568Ssam#define ADDSELECTOR(frm, sel) do { \ 1286138568Ssam memcpy(frm, sel, 4); \ 1287138568Ssam frm += 4; \ 1288138568Ssam} while (0) 1289170530Ssam static const uint8_t cipher_suite[][4] = { 1290138568Ssam { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */ 1291138568Ssam { RSN_OUI_BYTES, RSN_CSE_TKIP }, 1292138568Ssam { RSN_OUI_BYTES, RSN_CSE_WRAP }, 1293138568Ssam { RSN_OUI_BYTES, RSN_CSE_CCMP }, 1294138568Ssam { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ 1295138568Ssam { RSN_OUI_BYTES, RSN_CSE_NULL }, 1296138568Ssam }; 1297170530Ssam static const uint8_t wep104_suite[4] = 1298138568Ssam { RSN_OUI_BYTES, RSN_CSE_WEP104 }; 1299170530Ssam static const uint8_t key_mgt_unspec[4] = 1300138568Ssam { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC }; 1301170530Ssam static const uint8_t key_mgt_psk[4] = 1302138568Ssam { RSN_OUI_BYTES, RSN_ASE_8021X_PSK }; 1303138568Ssam const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 1304170530Ssam uint8_t *frm = ie; 1305170530Ssam uint8_t *selcnt; 1306138568Ssam 1307138568Ssam *frm++ = IEEE80211_ELEMID_RSN; 1308138568Ssam *frm++ = 0; /* length filled in below */ 1309138568Ssam ADDSHORT(frm, RSN_VERSION); 1310138568Ssam 1311138568Ssam /* XXX filter out CKIP */ 1312138568Ssam 1313138568Ssam /* multicast cipher */ 1314138568Ssam if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && 1315138568Ssam rsn->rsn_mcastkeylen >= 13) 1316138568Ssam ADDSELECTOR(frm, wep104_suite); 1317138568Ssam else 1318138568Ssam ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); 1319138568Ssam 1320138568Ssam /* unicast cipher list */ 1321138568Ssam selcnt = frm; 1322138568Ssam ADDSHORT(frm, 0); /* selector count */ 1323138568Ssam if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) { 1324138568Ssam selcnt[0]++; 1325138568Ssam ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); 1326138568Ssam } 1327138568Ssam if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { 1328138568Ssam selcnt[0]++; 1329138568Ssam ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); 1330138568Ssam } 1331138568Ssam 1332138568Ssam /* authenticator selector list */ 1333138568Ssam selcnt = frm; 1334138568Ssam ADDSHORT(frm, 0); /* selector count */ 1335138568Ssam if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { 1336138568Ssam selcnt[0]++; 1337138568Ssam ADDSELECTOR(frm, key_mgt_unspec); 1338138568Ssam } 1339138568Ssam if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { 1340138568Ssam selcnt[0]++; 1341138568Ssam ADDSELECTOR(frm, key_mgt_psk); 1342138568Ssam } 1343138568Ssam 1344138568Ssam /* optional capabilities */ 1345147066Ssam ADDSHORT(frm, rsn->rsn_caps); 1346138568Ssam /* XXX PMKID */ 1347138568Ssam 1348138568Ssam /* calculate element length */ 1349138568Ssam ie[1] = frm - ie - 2; 1350138568Ssam KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), 1351138609Ssam ("RSN IE too big, %u > %zu", 1352138568Ssam ie[1]+2, sizeof(struct ieee80211_ie_wpa))); 1353138568Ssam return frm; 1354138568Ssam#undef ADDSELECTOR 1355138568Ssam#undef ADDSHORT 1356138568Ssam#undef RSN_OUI_BYTES 1357138568Ssam} 1358138568Ssam 1359119150Ssam/* 1360138568Ssam * Add a WPA/RSN element to a frame. 1361138568Ssam */ 1362170530Ssamstatic uint8_t * 1363170530Ssamieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic) 1364138568Ssam{ 1365138568Ssam 1366138568Ssam KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!")); 1367138568Ssam if (ic->ic_flags & IEEE80211_F_WPA2) 1368138568Ssam frm = ieee80211_setup_rsn_ie(ic, frm); 1369138568Ssam if (ic->ic_flags & IEEE80211_F_WPA1) 1370138568Ssam frm = ieee80211_setup_wpa_ie(ic, frm); 1371138568Ssam return frm; 1372138568Ssam} 1373138568Ssam 1374138568Ssam#define WME_OUI_BYTES 0x00, 0x50, 0xf2 1375138568Ssam/* 1376138568Ssam * Add a WME information element to a frame. 1377138568Ssam */ 1378170530Ssamstatic uint8_t * 1379170530Ssamieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) 1380138568Ssam{ 1381138568Ssam static const struct ieee80211_wme_info info = { 1382138568Ssam .wme_id = IEEE80211_ELEMID_VENDOR, 1383138568Ssam .wme_len = sizeof(struct ieee80211_wme_info) - 2, 1384138568Ssam .wme_oui = { WME_OUI_BYTES }, 1385138568Ssam .wme_type = WME_OUI_TYPE, 1386138568Ssam .wme_subtype = WME_INFO_OUI_SUBTYPE, 1387138568Ssam .wme_version = WME_VERSION, 1388138568Ssam .wme_info = 0, 1389138568Ssam }; 1390138568Ssam memcpy(frm, &info, sizeof(info)); 1391138568Ssam return frm + sizeof(info); 1392138568Ssam} 1393138568Ssam 1394138568Ssam/* 1395138568Ssam * Add a WME parameters element to a frame. 1396138568Ssam */ 1397170530Ssamstatic uint8_t * 1398170530Ssamieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) 1399138568Ssam{ 1400138568Ssam#define SM(_v, _f) (((_v) << _f##_S) & _f) 1401138568Ssam#define ADDSHORT(frm, v) do { \ 1402138568Ssam frm[0] = (v) & 0xff; \ 1403138568Ssam frm[1] = (v) >> 8; \ 1404138568Ssam frm += 2; \ 1405138568Ssam} while (0) 1406138568Ssam /* NB: this works 'cuz a param has an info at the front */ 1407138568Ssam static const struct ieee80211_wme_info param = { 1408138568Ssam .wme_id = IEEE80211_ELEMID_VENDOR, 1409138568Ssam .wme_len = sizeof(struct ieee80211_wme_param) - 2, 1410138568Ssam .wme_oui = { WME_OUI_BYTES }, 1411138568Ssam .wme_type = WME_OUI_TYPE, 1412138568Ssam .wme_subtype = WME_PARAM_OUI_SUBTYPE, 1413138568Ssam .wme_version = WME_VERSION, 1414138568Ssam }; 1415138568Ssam int i; 1416138568Ssam 1417138568Ssam memcpy(frm, ¶m, sizeof(param)); 1418138568Ssam frm += __offsetof(struct ieee80211_wme_info, wme_info); 1419138568Ssam *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ 1420138568Ssam *frm++ = 0; /* reserved field */ 1421138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 1422138568Ssam const struct wmeParams *ac = 1423138568Ssam &wme->wme_bssChanParams.cap_wmeParams[i]; 1424138568Ssam *frm++ = SM(i, WME_PARAM_ACI) 1425138568Ssam | SM(ac->wmep_acm, WME_PARAM_ACM) 1426138568Ssam | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) 1427138568Ssam ; 1428138568Ssam *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) 1429138568Ssam | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) 1430138568Ssam ; 1431138568Ssam ADDSHORT(frm, ac->wmep_txopLimit); 1432138568Ssam } 1433138568Ssam return frm; 1434138568Ssam#undef SM 1435138568Ssam#undef ADDSHORT 1436138568Ssam} 1437138568Ssam#undef WME_OUI_BYTES 1438138568Ssam 1439170530Ssam#define ATH_OUI_BYTES 0x00, 0x03, 0x7f 1440138568Ssam/* 1441170530Ssam * Add a WME information element to a frame. 1442170530Ssam */ 1443170530Ssamstatic uint8_t * 1444170530Ssamieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix) 1445170530Ssam{ 1446170530Ssam static const struct ieee80211_ath_ie info = { 1447170530Ssam .ath_id = IEEE80211_ELEMID_VENDOR, 1448170530Ssam .ath_len = sizeof(struct ieee80211_ath_ie) - 2, 1449170530Ssam .ath_oui = { ATH_OUI_BYTES }, 1450170530Ssam .ath_oui_type = ATH_OUI_TYPE, 1451170530Ssam .ath_oui_subtype= ATH_OUI_SUBTYPE, 1452170530Ssam .ath_version = ATH_OUI_VERSION, 1453170530Ssam }; 1454170530Ssam struct ieee80211_ath_ie *ath = (struct ieee80211_ath_ie *) frm; 1455170530Ssam 1456170530Ssam memcpy(frm, &info, sizeof(info)); 1457170530Ssam ath->ath_capability = caps; 1458170530Ssam ath->ath_defkeyix[0] = (defkeyix & 0xff); 1459170530Ssam ath->ath_defkeyix[1] = ((defkeyix >> 8) & 0xff); 1460170530Ssam return frm + sizeof(info); 1461170530Ssam} 1462170530Ssam#undef ATH_OUI_BYTES 1463170530Ssam 1464170530Ssam/* 1465148315Ssam * Send a probe request frame with the specified ssid 1466148315Ssam * and any optional information element data. 1467148315Ssam */ 1468148315Ssamint 1469148315Ssamieee80211_send_probereq(struct ieee80211_node *ni, 1470170530Ssam const uint8_t sa[IEEE80211_ADDR_LEN], 1471170530Ssam const uint8_t da[IEEE80211_ADDR_LEN], 1472170530Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 1473170530Ssam const uint8_t *ssid, size_t ssidlen, 1474148315Ssam const void *optie, size_t optielen) 1475148315Ssam{ 1476148315Ssam struct ieee80211com *ic = ni->ni_ic; 1477148315Ssam struct ieee80211_frame *wh; 1478165569Ssam const struct ieee80211_rateset *rs; 1479148315Ssam struct mbuf *m; 1480170530Ssam uint8_t *frm; 1481148315Ssam 1482148315Ssam /* 1483148315Ssam * Hold a reference on the node so it doesn't go away until after 1484148315Ssam * the xmit is complete all the way in the driver. On error we 1485148315Ssam * will remove our reference. 1486148315Ssam */ 1487148315Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, 1488148315Ssam "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 1489148315Ssam __func__, __LINE__, 1490148315Ssam ni, ether_sprintf(ni->ni_macaddr), 1491148315Ssam ieee80211_node_refcnt(ni)+1); 1492148315Ssam ieee80211_ref_node(ni); 1493148315Ssam 1494148315Ssam /* 1495148315Ssam * prreq frame format 1496148315Ssam * [tlv] ssid 1497148315Ssam * [tlv] supported rates 1498148315Ssam * [tlv] extended supported rates 1499148315Ssam * [tlv] user-specified ie's 1500148315Ssam */ 1501148315Ssam m = ieee80211_getmgtframe(&frm, 1502170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1503148315Ssam 2 + IEEE80211_NWID_LEN 1504148315Ssam + 2 + IEEE80211_RATE_SIZE 1505148315Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 1506148315Ssam + (optie != NULL ? optielen : 0) 1507148315Ssam ); 1508148315Ssam if (m == NULL) { 1509148315Ssam ic->ic_stats.is_tx_nobuf++; 1510148315Ssam ieee80211_free_node(ni); 1511148315Ssam return ENOMEM; 1512148315Ssam } 1513148315Ssam 1514148315Ssam frm = ieee80211_add_ssid(frm, ssid, ssidlen); 1515165569Ssam rs = ieee80211_get_suprates(ic, ic->ic_curchan); 1516165569Ssam frm = ieee80211_add_rates(frm, rs); 1517165569Ssam frm = ieee80211_add_xrates(frm, rs); 1518148315Ssam 1519148315Ssam if (optie != NULL) { 1520148315Ssam memcpy(frm, optie, optielen); 1521148315Ssam frm += optielen; 1522148315Ssam } 1523170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1524148315Ssam 1525148315Ssam M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 1526148315Ssam if (m == NULL) 1527148315Ssam return ENOMEM; 1528148315Ssam KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); 1529148315Ssam m->m_pkthdr.rcvif = (void *)ni; 1530148315Ssam 1531148315Ssam wh = mtod(m, struct ieee80211_frame *); 1532148315Ssam ieee80211_send_setup(ic, ni, wh, 1533148315Ssam IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, 1534148315Ssam sa, da, bssid); 1535148315Ssam /* XXX power management? */ 1536148315Ssam 1537148315Ssam IEEE80211_NODE_STAT(ni, tx_probereq); 1538148315Ssam IEEE80211_NODE_STAT(ni, tx_mgmt); 1539148315Ssam 1540148315Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 1541148315Ssam "[%s] send probe req on channel %u\n", 1542148315Ssam ether_sprintf(wh->i_addr1), 1543148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan)); 1544148315Ssam 1545148315Ssam IF_ENQUEUE(&ic->ic_mgtq, m); 1546148315Ssam if_start(ic->ic_ifp); 1547148315Ssam return 0; 1548148315Ssam} 1549148315Ssam 1550148315Ssam/* 1551155999Ssam * Calculate capability information for mgt frames. 1552155999Ssam */ 1553170530Ssamstatic uint16_t 1554155999Ssamgetcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) 1555155999Ssam{ 1556170530Ssam uint16_t capinfo; 1557155999Ssam 1558155999Ssam KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode")); 1559155999Ssam 1560155999Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) 1561155999Ssam capinfo = IEEE80211_CAPINFO_ESS; 1562155999Ssam else if (ic->ic_opmode == IEEE80211_M_IBSS) 1563155999Ssam capinfo = IEEE80211_CAPINFO_IBSS; 1564155999Ssam else 1565155999Ssam capinfo = 0; 1566155999Ssam if (ic->ic_flags & IEEE80211_F_PRIVACY) 1567155999Ssam capinfo |= IEEE80211_CAPINFO_PRIVACY; 1568155999Ssam if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 1569155999Ssam IEEE80211_IS_CHAN_2GHZ(chan)) 1570155999Ssam capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 1571155999Ssam if (ic->ic_flags & IEEE80211_F_SHSLOT) 1572155999Ssam capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 1573155999Ssam return capinfo; 1574155999Ssam} 1575155999Ssam 1576155999Ssam/* 1577119150Ssam * Send a management frame. The node is for the destination (or ic_bss 1578119150Ssam * when in station mode). Nodes other than ic_bss have their reference 1579119150Ssam * count bumped to reflect our use for an indeterminant time. 1580119150Ssam */ 1581116742Ssamint 1582116742Ssamieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, 1583116742Ssam int type, int arg) 1584116742Ssam{ 1585121180Ssam#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) 1586116742Ssam struct mbuf *m; 1587170530Ssam uint8_t *frm; 1588170530Ssam uint16_t capinfo; 1589170530Ssam int has_challenge, is_shared_key, ret, status; 1590116742Ssam 1591119150Ssam KASSERT(ni != NULL, ("null node")); 1592119150Ssam 1593119150Ssam /* 1594119150Ssam * Hold a reference on the node so it doesn't go away until after 1595119150Ssam * the xmit is complete all the way in the driver. On error we 1596119150Ssam * will remove our reference. 1597119150Ssam */ 1598138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, 1599140766Ssam "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 1600138568Ssam __func__, __LINE__, 1601140766Ssam ni, ether_sprintf(ni->ni_macaddr), 1602140766Ssam ieee80211_node_refcnt(ni)+1); 1603138568Ssam ieee80211_ref_node(ni); 1604138568Ssam 1605116742Ssam switch (type) { 1606116742Ssam case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 1607116742Ssam /* 1608116742Ssam * probe response frame format 1609116742Ssam * [8] time stamp 1610116742Ssam * [2] beacon interval 1611116742Ssam * [2] cabability information 1612116742Ssam * [tlv] ssid 1613116742Ssam * [tlv] supported rates 1614121178Ssam * [tlv] parameter set (FH/DS) 1615116742Ssam * [tlv] parameter set (IBSS) 1616138568Ssam * [tlv] extended rate phy (ERP) 1617116742Ssam * [tlv] extended supported rates 1618138568Ssam * [tlv] WPA 1619144136Ssam * [tlv] WME (optional) 1620170530Ssam * [tlv] HT capabilities 1621170530Ssam * [tlv] HT information 1622170530Ssam * [tlv] Vendor OUI HT capabilities (optional) 1623170530Ssam * [tlv] Vendor OUI HT information (optional) 1624170530Ssam * [tlv] Atheros capabilities 1625116742Ssam */ 1626138568Ssam m = ieee80211_getmgtframe(&frm, 1627170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1628138568Ssam 8 1629170530Ssam + sizeof(uint16_t) 1630170530Ssam + sizeof(uint16_t) 1631138568Ssam + 2 + IEEE80211_NWID_LEN 1632116742Ssam + 2 + IEEE80211_RATE_SIZE 1633138568Ssam + 7 /* max(7,3) */ 1634116742Ssam + 6 1635138568Ssam + 3 1636138568Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 1637138568Ssam /* XXX !WPA1+WPA2 fits w/o a cluster */ 1638138568Ssam + (ic->ic_flags & IEEE80211_F_WPA ? 1639138568Ssam 2*sizeof(struct ieee80211_ie_wpa) : 0) 1640144136Ssam + sizeof(struct ieee80211_wme_param) 1641170530Ssam /* XXX check for cluster requirement */ 1642170530Ssam + 2*sizeof(struct ieee80211_ie_htcap) + 4 1643170530Ssam + 2*sizeof(struct ieee80211_ie_htinfo) + 4 1644170530Ssam + sizeof(struct ieee80211_ath_ie) 1645138568Ssam ); 1646116742Ssam if (m == NULL) 1647138568Ssam senderr(ENOMEM, is_tx_nobuf); 1648116742Ssam 1649116742Ssam memset(frm, 0, 8); /* timestamp should be filled later */ 1650116742Ssam frm += 8; 1651170530Ssam *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval); 1652116742Ssam frm += 2; 1653155999Ssam capinfo = getcapinfo(ic, ic->ic_curchan); 1654170530Ssam *(uint16_t *)frm = htole16(capinfo); 1655116742Ssam frm += 2; 1656116742Ssam 1657116742Ssam frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, 1658116742Ssam ic->ic_bss->ni_esslen); 1659138568Ssam frm = ieee80211_add_rates(frm, &ni->ni_rates); 1660116742Ssam 1661170530Ssam if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) { 1662121178Ssam *frm++ = IEEE80211_ELEMID_FHPARMS; 1663121178Ssam *frm++ = 5; 1664121178Ssam *frm++ = ni->ni_fhdwell & 0x00ff; 1665121178Ssam *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff; 1666121178Ssam *frm++ = IEEE80211_FH_CHANSET( 1667148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan)); 1668121178Ssam *frm++ = IEEE80211_FH_CHANPAT( 1669148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan)); 1670121178Ssam *frm++ = ni->ni_fhindex; 1671121178Ssam } else { 1672121178Ssam *frm++ = IEEE80211_ELEMID_DSPARMS; 1673121178Ssam *frm++ = 1; 1674148936Ssam *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan); 1675121178Ssam } 1676121178Ssam 1677116742Ssam if (ic->ic_opmode == IEEE80211_M_IBSS) { 1678116742Ssam *frm++ = IEEE80211_ELEMID_IBSSPARMS; 1679116742Ssam *frm++ = 2; 1680116742Ssam *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 1681116742Ssam } 1682138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) 1683138568Ssam frm = ieee80211_add_wpa(frm, ic); 1684170530Ssam if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) 1685138568Ssam frm = ieee80211_add_erp(frm, ic); 1686138568Ssam frm = ieee80211_add_xrates(frm, &ni->ni_rates); 1687144136Ssam if (ic->ic_flags & IEEE80211_F_WME) 1688144136Ssam frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 1689170530Ssam if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { 1690170530Ssam frm = ieee80211_add_htcap(frm, ni); 1691170530Ssam frm = ieee80211_add_htinfo(frm, ni); 1692170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { 1693170530Ssam frm = ieee80211_add_htcap_vendor(frm, ni); 1694170530Ssam frm = ieee80211_add_htinfo_vendor(frm, ni); 1695170530Ssam } 1696170530Ssam } 1697170530Ssam if (ni->ni_ath_ie != NULL) 1698170530Ssam frm = ieee80211_add_ath(frm, ni->ni_ath_flags, 1699170530Ssam ni->ni_ath_defkeyix); 1700170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1701116742Ssam break; 1702116742Ssam 1703116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1704138568Ssam status = arg >> 16; 1705138568Ssam arg &= 0xffff; 1706138568Ssam has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || 1707138568Ssam arg == IEEE80211_AUTH_SHARED_RESPONSE) && 1708138568Ssam ni->ni_challenge != NULL); 1709138568Ssam 1710138568Ssam /* 1711138568Ssam * Deduce whether we're doing open authentication or 1712138568Ssam * shared key authentication. We do the latter if 1713138568Ssam * we're in the middle of a shared key authentication 1714138568Ssam * handshake or if we're initiating an authentication 1715138568Ssam * request and configured to use shared key. 1716138568Ssam */ 1717138568Ssam is_shared_key = has_challenge || 1718138568Ssam arg >= IEEE80211_AUTH_SHARED_RESPONSE || 1719138568Ssam (arg == IEEE80211_AUTH_SHARED_REQUEST && 1720138568Ssam ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); 1721138568Ssam 1722138568Ssam m = ieee80211_getmgtframe(&frm, 1723170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1724170530Ssam 3 * sizeof(uint16_t) 1725138568Ssam + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? 1726170530Ssam sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) 1727138568Ssam ); 1728116742Ssam if (m == NULL) 1729138568Ssam senderr(ENOMEM, is_tx_nobuf); 1730138568Ssam 1731170530Ssam ((uint16_t *)frm)[0] = 1732138568Ssam (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) 1733138568Ssam : htole16(IEEE80211_AUTH_ALG_OPEN); 1734170530Ssam ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */ 1735170530Ssam ((uint16_t *)frm)[2] = htole16(status);/* status */ 1736138568Ssam 1737138568Ssam if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { 1738170530Ssam ((uint16_t *)frm)[3] = 1739138568Ssam htole16((IEEE80211_CHALLENGE_LEN << 8) | 1740138568Ssam IEEE80211_ELEMID_CHALLENGE); 1741170530Ssam memcpy(&((uint16_t *)frm)[4], ni->ni_challenge, 1742138568Ssam IEEE80211_CHALLENGE_LEN); 1743138568Ssam m->m_pkthdr.len = m->m_len = 1744170530Ssam 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; 1745138568Ssam if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { 1746138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 1747138568Ssam "[%s] request encrypt frame (%s)\n", 1748138568Ssam ether_sprintf(ni->ni_macaddr), __func__); 1749138568Ssam m->m_flags |= M_LINK0; /* WEP-encrypt, please */ 1750138568Ssam } 1751138568Ssam } else 1752170530Ssam m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t); 1753138568Ssam 1754138568Ssam /* XXX not right for shared key */ 1755138568Ssam if (status == IEEE80211_STATUS_SUCCESS) 1756138568Ssam IEEE80211_NODE_STAT(ni, tx_auth); 1757138568Ssam else 1758138568Ssam IEEE80211_NODE_STAT(ni, tx_auth_fail); 1759138568Ssam 1760116742Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1761170530Ssam ieee80211_add_callback(m, ieee80211_tx_mgt_cb, 1762170530Ssam (void *) ic->ic_state); 1763116742Ssam break; 1764116742Ssam 1765116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1766138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 1767138568Ssam "[%s] send station deauthenticate (reason %d)\n", 1768138568Ssam ether_sprintf(ni->ni_macaddr), arg); 1769170530Ssam m = ieee80211_getmgtframe(&frm, 1770170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1771170530Ssam sizeof(uint16_t)); 1772116742Ssam if (m == NULL) 1773138568Ssam senderr(ENOMEM, is_tx_nobuf); 1774170530Ssam *(uint16_t *)frm = htole16(arg); /* reason */ 1775170530Ssam m->m_pkthdr.len = m->m_len = sizeof(uint16_t); 1776138568Ssam 1777138568Ssam IEEE80211_NODE_STAT(ni, tx_deauth); 1778138568Ssam IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); 1779138568Ssam 1780148302Ssam ieee80211_node_unauthorize(ni); /* port closed */ 1781116742Ssam break; 1782116742Ssam 1783116742Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 1784116742Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 1785116742Ssam /* 1786116742Ssam * asreq frame format 1787116742Ssam * [2] capability information 1788116742Ssam * [2] listen interval 1789116742Ssam * [6*] current AP address (reassoc only) 1790116742Ssam * [tlv] ssid 1791116742Ssam * [tlv] supported rates 1792116742Ssam * [tlv] extended supported rates 1793138568Ssam * [tlv] WME 1794170530Ssam * [tlv] HT capabilities 1795170530Ssam * [tlv] Vendor OUI HT capabilities (optional) 1796170530Ssam * [tlv] Atheros capabilities (if negotiated) 1797138568Ssam * [tlv] user-specified ie's 1798116742Ssam */ 1799138568Ssam m = ieee80211_getmgtframe(&frm, 1800170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1801170530Ssam sizeof(uint16_t) 1802170530Ssam + sizeof(uint16_t) 1803116742Ssam + IEEE80211_ADDR_LEN 1804138568Ssam + 2 + IEEE80211_NWID_LEN 1805116742Ssam + 2 + IEEE80211_RATE_SIZE 1806138568Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 1807138568Ssam + sizeof(struct ieee80211_wme_info) 1808170530Ssam + 2*sizeof(struct ieee80211_ie_htcap) + 4 1809170530Ssam + sizeof(struct ieee80211_ath_ie) 1810138568Ssam + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) 1811138568Ssam ); 1812116742Ssam if (m == NULL) 1813138568Ssam senderr(ENOMEM, is_tx_nobuf); 1814116742Ssam 1815155999Ssam KASSERT(ic->ic_opmode == IEEE80211_M_STA, 1816155999Ssam ("wrong mode %u", ic->ic_opmode)); 1817155999Ssam capinfo = IEEE80211_CAPINFO_ESS; 1818138568Ssam if (ic->ic_flags & IEEE80211_F_PRIVACY) 1819116742Ssam capinfo |= IEEE80211_CAPINFO_PRIVACY; 1820120070Ssam /* 1821120070Ssam * NB: Some 11a AP's reject the request when 1822120070Ssam * short premable is set. 1823120070Ssam */ 1824120070Ssam if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 1825148936Ssam IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 1826116742Ssam capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 1827170530Ssam if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 1828138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT)) 1829116742Ssam capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 1830170530Ssam *(uint16_t *)frm = htole16(capinfo); 1831116742Ssam frm += 2; 1832116742Ssam 1833170530Ssam KASSERT(ic->ic_bss->ni_intval != 0, 1834170530Ssam ("beacon interval is zero!")); 1835170530Ssam *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, 1836170530Ssam ic->ic_bss->ni_intval)); 1837116742Ssam frm += 2; 1838116742Ssam 1839116742Ssam if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 1840116742Ssam IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); 1841116742Ssam frm += IEEE80211_ADDR_LEN; 1842116742Ssam } 1843116742Ssam 1844116742Ssam frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 1845116742Ssam frm = ieee80211_add_rates(frm, &ni->ni_rates); 1846116742Ssam frm = ieee80211_add_xrates(frm, &ni->ni_rates); 1847138568Ssam if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) 1848138568Ssam frm = ieee80211_add_wme_info(frm, &ic->ic_wme); 1849170530Ssam if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { 1850170530Ssam frm = ieee80211_add_htcap(frm, ni); 1851170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) 1852170530Ssam frm = ieee80211_add_htcap_vendor(frm, ni); 1853170530Ssam } 1854170530Ssam if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) 1855170530Ssam frm = ieee80211_add_ath(frm, 1856170530Ssam IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), 1857170530Ssam (ic->ic_flags & IEEE80211_F_WPA) == 0 && 1858170530Ssam ni->ni_authmode != IEEE80211_AUTH_8021X && 1859170530Ssam ic->ic_def_txkey != IEEE80211_KEYIX_NONE ? 1860170530Ssam ic->ic_def_txkey : 0x7fff); 1861138568Ssam if (ic->ic_opt_ie != NULL) { 1862138568Ssam memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); 1863138568Ssam frm += ic->ic_opt_ie_len; 1864138568Ssam } 1865170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1866116742Ssam 1867170530Ssam ieee80211_add_callback(m, ieee80211_tx_mgt_cb, 1868170530Ssam (void *) ic->ic_state); 1869116742Ssam break; 1870116742Ssam 1871116742Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 1872116742Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 1873116742Ssam /* 1874170530Ssam * asresp frame format 1875116742Ssam * [2] capability information 1876116742Ssam * [2] status 1877116742Ssam * [2] association ID 1878116742Ssam * [tlv] supported rates 1879116742Ssam * [tlv] extended supported rates 1880138568Ssam * [tlv] WME (if enabled and STA enabled) 1881170530Ssam * [tlv] HT capabilities (standard or vendor OUI) 1882170530Ssam * [tlv] HT information (standard or vendor OUI) 1883170530Ssam * [tlv] Atheros capabilities (if enabled and STA enabled) 1884116742Ssam */ 1885138568Ssam m = ieee80211_getmgtframe(&frm, 1886170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1887170530Ssam sizeof(uint16_t) 1888170530Ssam + sizeof(uint16_t) 1889170530Ssam + sizeof(uint16_t) 1890116742Ssam + 2 + IEEE80211_RATE_SIZE 1891138568Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 1892138568Ssam + sizeof(struct ieee80211_wme_param) 1893170530Ssam + sizeof(struct ieee80211_ie_htcap) + 4 1894170530Ssam + sizeof(struct ieee80211_ie_htinfo) + 4 1895170530Ssam + sizeof(struct ieee80211_ath_ie) 1896138568Ssam ); 1897116742Ssam if (m == NULL) 1898138568Ssam senderr(ENOMEM, is_tx_nobuf); 1899116742Ssam 1900155999Ssam capinfo = getcapinfo(ic, ic->ic_curchan); 1901170530Ssam *(uint16_t *)frm = htole16(capinfo); 1902116742Ssam frm += 2; 1903116742Ssam 1904170530Ssam *(uint16_t *)frm = htole16(arg); /* status */ 1905116742Ssam frm += 2; 1906116742Ssam 1907138568Ssam if (arg == IEEE80211_STATUS_SUCCESS) { 1908170530Ssam *(uint16_t *)frm = htole16(ni->ni_associd); 1909138568Ssam IEEE80211_NODE_STAT(ni, tx_assoc); 1910138568Ssam } else 1911138568Ssam IEEE80211_NODE_STAT(ni, tx_assoc_fail); 1912116742Ssam frm += 2; 1913116742Ssam 1914119150Ssam frm = ieee80211_add_rates(frm, &ni->ni_rates); 1915119150Ssam frm = ieee80211_add_xrates(frm, &ni->ni_rates); 1916138568Ssam if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) 1917138568Ssam frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 1918170530Ssam if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { 1919170530Ssam /* NB: respond according to what we received */ 1920170530Ssam if (ni->ni_flags & IEEE80211_NODE_HTCOMPAT) { 1921170530Ssam frm = ieee80211_add_htcap_vendor(frm, ni); 1922170530Ssam frm = ieee80211_add_htinfo_vendor(frm, ni); 1923170530Ssam } else { 1924170530Ssam frm = ieee80211_add_htcap(frm, ni); 1925170530Ssam frm = ieee80211_add_htinfo(frm, ni); 1926170530Ssam } 1927170530Ssam } 1928170530Ssam if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS)) 1929170530Ssam frm = ieee80211_add_ath(frm, 1930170530Ssam IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS), 1931170530Ssam ni->ni_ath_defkeyix); 1932170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 1933116742Ssam break; 1934116742Ssam 1935116742Ssam case IEEE80211_FC0_SUBTYPE_DISASSOC: 1936138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 1937138568Ssam "[%s] send station disassociate (reason %d)\n", 1938138568Ssam ether_sprintf(ni->ni_macaddr), arg); 1939170530Ssam m = ieee80211_getmgtframe(&frm, 1940170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), 1941170530Ssam sizeof(uint16_t)); 1942116742Ssam if (m == NULL) 1943138568Ssam senderr(ENOMEM, is_tx_nobuf); 1944170530Ssam *(uint16_t *)frm = htole16(arg); /* reason */ 1945170530Ssam m->m_pkthdr.len = m->m_len = sizeof(uint16_t); 1946138568Ssam 1947138568Ssam IEEE80211_NODE_STAT(ni, tx_disassoc); 1948138568Ssam IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); 1949116742Ssam break; 1950116742Ssam 1951116742Ssam default: 1952138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1953138568Ssam "[%s] invalid mgmt frame type %u\n", 1954138568Ssam ether_sprintf(ni->ni_macaddr), type); 1955121180Ssam senderr(EINVAL, is_tx_unknownmgt); 1956119150Ssam /* NOTREACHED */ 1957116742Ssam } 1958170530Ssam 1959170530Ssam ret = ieee80211_mgmt_output(ic, ni, m, type); 1960170530Ssam if (ret != 0) 1961170530Ssam goto bad; 1962170530Ssam return 0; 1963119150Ssambad: 1964170530Ssam ieee80211_free_node(ni); 1965116742Ssam return ret; 1966119150Ssam#undef senderr 1967116742Ssam} 1968138568Ssam 1969170530Ssamstatic void 1970170530Ssamieee80211_tx_mgt_timeout(void *arg) 1971170530Ssam{ 1972170530Ssam struct ieee80211_node *ni = arg; 1973170530Ssam struct ieee80211com *ic = ni->ni_ic; 1974170530Ssam 1975170530Ssam if (ic->ic_state != IEEE80211_S_INIT && 1976170530Ssam (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 1977170530Ssam /* 1978170530Ssam * NB: it's safe to specify a timeout as the reason here; 1979170530Ssam * it'll only be used in the right state. 1980170530Ssam */ 1981170530Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 1982170530Ssam IEEE80211_SCAN_FAIL_TIMEOUT); 1983170530Ssam } 1984170530Ssam} 1985170530Ssam 1986170530Ssamstatic void 1987170530Ssamieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) 1988170530Ssam{ 1989170530Ssam struct ieee80211com *ic = ni->ni_ic; 1990170530Ssam enum ieee80211_state ostate = (enum ieee80211_state) arg; 1991170530Ssam 1992170530Ssam /* 1993170530Ssam * Frame transmit completed; arrange timer callback. If 1994170530Ssam * transmit was successfuly we wait for response. Otherwise 1995170530Ssam * we arrange an immediate callback instead of doing the 1996170530Ssam * callback directly since we don't know what state the driver 1997170530Ssam * is in (e.g. what locks it is holding). This work should 1998170530Ssam * not be too time-critical and not happen too often so the 1999170530Ssam * added overhead is acceptable. 2000170530Ssam * 2001170530Ssam * XXX what happens if !acked but response shows up before callback? 2002170530Ssam */ 2003170530Ssam if (ic->ic_state == ostate) 2004170530Ssam callout_reset(&ic->ic_mgtsend, 2005170530Ssam status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, 2006170530Ssam ieee80211_tx_mgt_timeout, ni); 2007170530Ssam} 2008170530Ssam 2009138568Ssam/* 2010138568Ssam * Allocate a beacon frame and fillin the appropriate bits. 2011138568Ssam */ 2012138568Ssamstruct mbuf * 2013172211Ssamieee80211_beacon_alloc(struct ieee80211_node *ni, 2014138568Ssam struct ieee80211_beacon_offsets *bo) 2015138568Ssam{ 2016172211Ssam struct ieee80211com *ic = ni->ni_ic; 2017138568Ssam struct ifnet *ifp = ic->ic_ifp; 2018138568Ssam struct ieee80211_frame *wh; 2019138568Ssam struct mbuf *m; 2020138568Ssam int pktlen; 2021170530Ssam uint8_t *frm; 2022170530Ssam uint16_t capinfo; 2023138568Ssam struct ieee80211_rateset *rs; 2024138568Ssam 2025138568Ssam /* 2026138568Ssam * beacon frame format 2027138568Ssam * [8] time stamp 2028138568Ssam * [2] beacon interval 2029138568Ssam * [2] cabability information 2030138568Ssam * [tlv] ssid 2031138568Ssam * [tlv] supported rates 2032138568Ssam * [3] parameter set (DS) 2033138568Ssam * [tlv] parameter set (IBSS/TIM) 2034170530Ssam * [tlv] country code 2035138568Ssam * [tlv] extended rate phy (ERP) 2036138568Ssam * [tlv] extended supported rates 2037138568Ssam * [tlv] WME parameters 2038138568Ssam * [tlv] WPA/RSN parameters 2039170530Ssam * [tlv] HT capabilities 2040170530Ssam * [tlv] HT information 2041170530Ssam * [tlv] Vendor OUI HT capabilities (optional) 2042170530Ssam * [tlv] Vendor OUI HT information (optional) 2043138568Ssam * XXX Vendor-specific OIDs (e.g. Atheros) 2044138568Ssam * NB: we allocate the max space required for the TIM bitmap. 2045138568Ssam */ 2046138568Ssam rs = &ni->ni_rates; 2047138568Ssam pktlen = 8 /* time stamp */ 2048170530Ssam + sizeof(uint16_t) /* beacon interval */ 2049170530Ssam + sizeof(uint16_t) /* capabilities */ 2050138568Ssam + 2 + ni->ni_esslen /* ssid */ 2051138568Ssam + 2 + IEEE80211_RATE_SIZE /* supported rates */ 2052138568Ssam + 2 + 1 /* DS parameters */ 2053138568Ssam + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */ 2054170530Ssam + sizeof(struct ieee80211_country_ie) /* country code */ 2055138568Ssam + 2 + 1 /* ERP */ 2056138568Ssam + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 2057138568Ssam + (ic->ic_caps & IEEE80211_C_WME ? /* WME */ 2058138568Ssam sizeof(struct ieee80211_wme_param) : 0) 2059138568Ssam + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ 2060138568Ssam 2*sizeof(struct ieee80211_ie_wpa) : 0) 2061170530Ssam /* XXX conditional? */ 2062170530Ssam + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ 2063170530Ssam + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ 2064138568Ssam ; 2065170530Ssam m = ieee80211_getmgtframe(&frm, 2066170530Ssam ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); 2067138568Ssam if (m == NULL) { 2068138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 2069138568Ssam "%s: cannot get buf; size %u\n", __func__, pktlen); 2070138568Ssam ic->ic_stats.is_tx_nobuf++; 2071138568Ssam return NULL; 2072138568Ssam } 2073138568Ssam 2074138568Ssam memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ 2075138568Ssam frm += 8; 2076170530Ssam *(uint16_t *)frm = htole16(ni->ni_intval); 2077138568Ssam frm += 2; 2078155999Ssam capinfo = getcapinfo(ic, ni->ni_chan); 2079170530Ssam bo->bo_caps = (uint16_t *)frm; 2080170530Ssam *(uint16_t *)frm = htole16(capinfo); 2081138568Ssam frm += 2; 2082138568Ssam *frm++ = IEEE80211_ELEMID_SSID; 2083138568Ssam if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) { 2084138568Ssam *frm++ = ni->ni_esslen; 2085138568Ssam memcpy(frm, ni->ni_essid, ni->ni_esslen); 2086138568Ssam frm += ni->ni_esslen; 2087138568Ssam } else 2088138568Ssam *frm++ = 0; 2089138568Ssam frm = ieee80211_add_rates(frm, rs); 2090170530Ssam if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) { 2091138568Ssam *frm++ = IEEE80211_ELEMID_DSPARMS; 2092138568Ssam *frm++ = 1; 2093170530Ssam *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan); 2094138568Ssam } 2095138568Ssam bo->bo_tim = frm; 2096138568Ssam if (ic->ic_opmode == IEEE80211_M_IBSS) { 2097138568Ssam *frm++ = IEEE80211_ELEMID_IBSSPARMS; 2098138568Ssam *frm++ = 2; 2099138568Ssam *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 2100138568Ssam bo->bo_tim_len = 0; 2101155999Ssam } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 2102138568Ssam struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; 2103138568Ssam 2104138568Ssam tie->tim_ie = IEEE80211_ELEMID_TIM; 2105138568Ssam tie->tim_len = 4; /* length */ 2106138568Ssam tie->tim_count = 0; /* DTIM count */ 2107138568Ssam tie->tim_period = ic->ic_dtim_period; /* DTIM period */ 2108138568Ssam tie->tim_bitctl = 0; /* bitmap control */ 2109138568Ssam tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ 2110138568Ssam frm += sizeof(struct ieee80211_tim_ie); 2111138568Ssam bo->bo_tim_len = 1; 2112138568Ssam } 2113172211Ssam bo->bo_tim_trailer = frm; 2114170530Ssam if (ic->ic_flags & IEEE80211_F_DOTH) 2115170530Ssam frm = ieee80211_add_countryie(frm, ic, 2116170530Ssam ic->ic_countrycode, ic->ic_location); 2117138568Ssam if (ic->ic_flags & IEEE80211_F_WME) { 2118138568Ssam bo->bo_wme = frm; 2119138568Ssam frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 2120170530Ssam } else 2121170530Ssam bo->bo_wme = NULL; 2122138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) 2123138568Ssam frm = ieee80211_add_wpa(frm, ic); 2124170530Ssam if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) { 2125153973Ssam bo->bo_erp = frm; 2126138568Ssam frm = ieee80211_add_erp(frm, ic); 2127170530Ssam } else 2128170530Ssam bo->bo_erp = NULL; 2129170530Ssam frm = ieee80211_add_xrates(frm, rs); 2130170530Ssam if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) { 2131170530Ssam frm = ieee80211_add_htcap(frm, ni); 2132170530Ssam bo->bo_htinfo = frm; 2133170530Ssam frm = ieee80211_add_htinfo(frm, ni); 2134170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) { 2135170530Ssam frm = ieee80211_add_htcap_vendor(frm, ni); 2136170530Ssam frm = ieee80211_add_htinfo_vendor(frm, ni); 2137170530Ssam } 2138170530Ssam } else 2139170530Ssam bo->bo_htinfo = NULL; 2140172211Ssam bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer; 2141170530Ssam m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2142138568Ssam 2143138568Ssam M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 2144138568Ssam KASSERT(m != NULL, ("no space for 802.11 header?")); 2145138568Ssam wh = mtod(m, struct ieee80211_frame *); 2146138568Ssam wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 2147138568Ssam IEEE80211_FC0_SUBTYPE_BEACON; 2148138568Ssam wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 2149170530Ssam *(uint16_t *)wh->i_dur = 0; 2150138568Ssam IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 2151138568Ssam IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 2152138568Ssam IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 2153170530Ssam *(uint16_t *)wh->i_seq = 0; 2154138568Ssam 2155138568Ssam return m; 2156138568Ssam} 2157138568Ssam 2158138568Ssam/* 2159138568Ssam * Update the dynamic parts of a beacon frame based on the current state. 2160138568Ssam */ 2161138568Ssamint 2162172211Ssamieee80211_beacon_update(struct ieee80211_node *ni, 2163138568Ssam struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) 2164138568Ssam{ 2165172211Ssam struct ieee80211com *ic = ni->ni_ic; 2166138568Ssam int len_changed = 0; 2167170530Ssam uint16_t capinfo; 2168138568Ssam 2169138568Ssam IEEE80211_BEACON_LOCK(ic); 2170138568Ssam /* XXX faster to recalculate entirely or just changes? */ 2171155999Ssam capinfo = getcapinfo(ic, ni->ni_chan); 2172138568Ssam *bo->bo_caps = htole16(capinfo); 2173138568Ssam 2174138568Ssam if (ic->ic_flags & IEEE80211_F_WME) { 2175138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 2176138568Ssam 2177138568Ssam /* 2178138568Ssam * Check for agressive mode change. When there is 2179138568Ssam * significant high priority traffic in the BSS 2180138568Ssam * throttle back BE traffic by using conservative 2181138568Ssam * parameters. Otherwise BE uses agressive params 2182138568Ssam * to optimize performance of legacy/non-QoS traffic. 2183138568Ssam */ 2184138568Ssam if (wme->wme_flags & WME_F_AGGRMODE) { 2185138568Ssam if (wme->wme_hipri_traffic > 2186138568Ssam wme->wme_hipri_switch_thresh) { 2187138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 2188138568Ssam "%s: traffic %u, disable aggressive mode\n", 2189138568Ssam __func__, wme->wme_hipri_traffic); 2190138568Ssam wme->wme_flags &= ~WME_F_AGGRMODE; 2191138568Ssam ieee80211_wme_updateparams_locked(ic); 2192138568Ssam wme->wme_hipri_traffic = 2193138568Ssam wme->wme_hipri_switch_hysteresis; 2194138568Ssam } else 2195138568Ssam wme->wme_hipri_traffic = 0; 2196138568Ssam } else { 2197138568Ssam if (wme->wme_hipri_traffic <= 2198138568Ssam wme->wme_hipri_switch_thresh) { 2199138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 2200138568Ssam "%s: traffic %u, enable aggressive mode\n", 2201138568Ssam __func__, wme->wme_hipri_traffic); 2202138568Ssam wme->wme_flags |= WME_F_AGGRMODE; 2203138568Ssam ieee80211_wme_updateparams_locked(ic); 2204138568Ssam wme->wme_hipri_traffic = 0; 2205138568Ssam } else 2206138568Ssam wme->wme_hipri_traffic = 2207138568Ssam wme->wme_hipri_switch_hysteresis; 2208138568Ssam } 2209172211Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) { 2210138568Ssam (void) ieee80211_add_wme_param(bo->bo_wme, wme); 2211172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_WME); 2212138568Ssam } 2213138568Ssam } 2214138568Ssam 2215172211Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { 2216172211Ssam ieee80211_ht_update_beacon(ic, bo); 2217172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO); 2218170530Ssam } 2219170530Ssam 2220138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ 2221138568Ssam struct ieee80211_tim_ie *tie = 2222138568Ssam (struct ieee80211_tim_ie *) bo->bo_tim; 2223172211Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) { 2224138568Ssam u_int timlen, timoff, i; 2225138568Ssam /* 2226138568Ssam * ATIM/DTIM needs updating. If it fits in the 2227138568Ssam * current space allocated then just copy in the 2228138568Ssam * new bits. Otherwise we need to move any trailing 2229138568Ssam * data to make room. Note that we know there is 2230138568Ssam * contiguous space because ieee80211_beacon_allocate 2231138568Ssam * insures there is space in the mbuf to write a 2232138568Ssam * maximal-size virtual bitmap (based on ic_max_aid). 2233138568Ssam */ 2234138568Ssam /* 2235138568Ssam * Calculate the bitmap size and offset, copy any 2236138568Ssam * trailer out of the way, and then copy in the 2237138568Ssam * new bitmap and update the information element. 2238138568Ssam * Note that the tim bitmap must contain at least 2239138568Ssam * one byte and any offset must be even. 2240138568Ssam */ 2241138568Ssam if (ic->ic_ps_pending != 0) { 2242138568Ssam timoff = 128; /* impossibly large */ 2243138568Ssam for (i = 0; i < ic->ic_tim_len; i++) 2244138568Ssam if (ic->ic_tim_bitmap[i]) { 2245138568Ssam timoff = i &~ 1; 2246138568Ssam break; 2247138568Ssam } 2248138568Ssam KASSERT(timoff != 128, ("tim bitmap empty!")); 2249138568Ssam for (i = ic->ic_tim_len-1; i >= timoff; i--) 2250138568Ssam if (ic->ic_tim_bitmap[i]) 2251138568Ssam break; 2252138568Ssam timlen = 1 + (i - timoff); 2253138568Ssam } else { 2254138568Ssam timoff = 0; 2255138568Ssam timlen = 1; 2256138568Ssam } 2257138568Ssam if (timlen != bo->bo_tim_len) { 2258138568Ssam /* copy up/down trailer */ 2259153973Ssam int adjust = tie->tim_bitmap+timlen 2260172211Ssam - bo->bo_tim_trailer; 2261172211Ssam ovbcopy(bo->bo_tim_trailer, 2262172211Ssam bo->bo_tim_trailer+adjust, 2263172211Ssam bo->bo_tim_trailer_len); 2264172211Ssam bo->bo_tim_trailer += adjust; 2265153973Ssam bo->bo_wme += adjust; 2266153973Ssam bo->bo_erp += adjust; 2267170530Ssam bo->bo_htinfo += adjust; 2268138568Ssam bo->bo_tim_len = timlen; 2269138568Ssam 2270138568Ssam /* update information element */ 2271138568Ssam tie->tim_len = 3 + timlen; 2272138568Ssam tie->tim_bitctl = timoff; 2273138568Ssam len_changed = 1; 2274138568Ssam } 2275138568Ssam memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff, 2276138568Ssam bo->bo_tim_len); 2277138568Ssam 2278172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_TIM); 2279138568Ssam 2280138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 2281138568Ssam "%s: TIM updated, pending %u, off %u, len %u\n", 2282138568Ssam __func__, ic->ic_ps_pending, timoff, timlen); 2283138568Ssam } 2284138568Ssam /* count down DTIM period */ 2285138568Ssam if (tie->tim_count == 0) 2286138568Ssam tie->tim_count = tie->tim_period - 1; 2287138568Ssam else 2288138568Ssam tie->tim_count--; 2289138568Ssam /* update state for buffered multicast frames on DTIM */ 2290153139Ssam if (mcast && tie->tim_count == 0) 2291138568Ssam tie->tim_bitctl |= 1; 2292138568Ssam else 2293138568Ssam tie->tim_bitctl &= ~1; 2294172211Ssam if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { 2295153973Ssam /* 2296153973Ssam * ERP element needs updating. 2297153973Ssam */ 2298153973Ssam (void) ieee80211_add_erp(bo->bo_erp, ic); 2299172211Ssam clrbit(bo->bo_flags, IEEE80211_BEACON_ERP); 2300153973Ssam } 2301138568Ssam } 2302138568Ssam IEEE80211_BEACON_UNLOCK(ic); 2303138568Ssam 2304138568Ssam return len_changed; 2305138568Ssam} 2306