11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 31f1d7810SSam Leffler * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting 41a1e1d21SSam Leffler * All rights reserved. 51a1e1d21SSam Leffler * 61a1e1d21SSam Leffler * Redistribution and use in source and binary forms, with or without 71a1e1d21SSam Leffler * modification, are permitted provided that the following conditions 81a1e1d21SSam Leffler * are met: 91a1e1d21SSam Leffler * 1. Redistributions of source code must retain the above copyright 107535e66aSSam Leffler * notice, this list of conditions and the following disclaimer. 117535e66aSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 127535e66aSSam Leffler * notice, this list of conditions and the following disclaimer in the 137535e66aSSam Leffler * documentation and/or other materials provided with the distribution. 147535e66aSSam Leffler * 3. The name of the author may not be used to endorse or promote products 157535e66aSSam Leffler * derived from this software without specific prior written permission. 161a1e1d21SSam Leffler * 171a1e1d21SSam Leffler * Alternatively, this software may be distributed under the terms of the 181a1e1d21SSam Leffler * GNU General Public License ("GPL") version 2 as published by the Free 191a1e1d21SSam Leffler * Software Foundation. 201a1e1d21SSam Leffler * 217535e66aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 227535e66aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 237535e66aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 247535e66aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 257535e66aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 267535e66aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 277535e66aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 287535e66aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 297535e66aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 307535e66aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 311a1e1d21SSam Leffler */ 321a1e1d21SSam Leffler 331a1e1d21SSam Leffler #include <sys/cdefs.h> 341a1e1d21SSam Leffler __FBSDID("$FreeBSD$"); 351a1e1d21SSam Leffler 361a1e1d21SSam Leffler #include "opt_inet.h" 371a1e1d21SSam Leffler 381a1e1d21SSam Leffler #include <sys/param.h> 391a1e1d21SSam Leffler #include <sys/systm.h> 401a1e1d21SSam Leffler #include <sys/mbuf.h> 411a1e1d21SSam Leffler #include <sys/kernel.h> 421a1e1d21SSam Leffler #include <sys/endian.h> 431a1e1d21SSam Leffler 448a1b9b6aSSam Leffler #include <sys/socket.h> 451a1e1d21SSam Leffler 461a1e1d21SSam Leffler #include <net/bpf.h> 478a1b9b6aSSam Leffler #include <net/ethernet.h> 488a1b9b6aSSam Leffler #include <net/if.h> 498a1b9b6aSSam Leffler #include <net/if_llc.h> 508a1b9b6aSSam Leffler #include <net/if_media.h> 518a1b9b6aSSam Leffler #include <net/if_vlan_var.h> 528a1b9b6aSSam Leffler 538a1b9b6aSSam Leffler #include <net80211/ieee80211_var.h> 541a1e1d21SSam Leffler 551a1e1d21SSam Leffler #ifdef INET 561a1e1d21SSam Leffler #include <netinet/in.h> 571a1e1d21SSam Leffler #include <netinet/if_ether.h> 588a1b9b6aSSam Leffler #include <netinet/in_systm.h> 598a1b9b6aSSam Leffler #include <netinet/ip.h> 608a1b9b6aSSam Leffler #endif 618a1b9b6aSSam Leffler 628a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 638a1b9b6aSSam Leffler /* 648a1b9b6aSSam Leffler * Decide if an outbound management frame should be 658a1b9b6aSSam Leffler * printed when debugging is enabled. This filters some 668a1b9b6aSSam Leffler * of the less interesting frames that come frequently 678a1b9b6aSSam Leffler * (e.g. beacons). 688a1b9b6aSSam Leffler */ 698a1b9b6aSSam Leffler static __inline int 708a1b9b6aSSam Leffler doprint(struct ieee80211com *ic, int subtype) 718a1b9b6aSSam Leffler { 728a1b9b6aSSam Leffler switch (subtype) { 738a1b9b6aSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 748a1b9b6aSSam Leffler return (ic->ic_opmode == IEEE80211_M_IBSS); 758a1b9b6aSSam Leffler } 768a1b9b6aSSam Leffler return 1; 778a1b9b6aSSam Leffler } 781a1e1d21SSam Leffler #endif 791a1e1d21SSam Leffler 800a915fadSSam Leffler /* 81add59d08SSam Leffler * Set the direction field and address fields of an outgoing 82add59d08SSam Leffler * non-QoS frame. Note this should be called early on in 83add59d08SSam Leffler * constructing a frame as it sets i_fc[1]; other bits can 84add59d08SSam Leffler * then be or'd in. 85add59d08SSam Leffler */ 86add59d08SSam Leffler static void 87add59d08SSam Leffler ieee80211_send_setup(struct ieee80211com *ic, 88add59d08SSam Leffler struct ieee80211_node *ni, 89add59d08SSam Leffler struct ieee80211_frame *wh, 90add59d08SSam Leffler int type, 91add59d08SSam Leffler const u_int8_t sa[IEEE80211_ADDR_LEN], 92add59d08SSam Leffler const u_int8_t da[IEEE80211_ADDR_LEN], 93add59d08SSam Leffler const u_int8_t bssid[IEEE80211_ADDR_LEN]) 94add59d08SSam Leffler { 95add59d08SSam Leffler #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) 96add59d08SSam Leffler 97add59d08SSam Leffler wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; 98add59d08SSam Leffler if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { 99add59d08SSam Leffler switch (ic->ic_opmode) { 100add59d08SSam Leffler case IEEE80211_M_STA: 101add59d08SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 102add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, bssid); 103add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, sa); 104add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, da); 105add59d08SSam Leffler break; 106add59d08SSam Leffler case IEEE80211_M_IBSS: 107add59d08SSam Leffler case IEEE80211_M_AHDEMO: 108add59d08SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 109add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, da); 110add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, sa); 111add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 112add59d08SSam Leffler break; 113add59d08SSam Leffler case IEEE80211_M_HOSTAP: 114add59d08SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 115add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, da); 116add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, bssid); 117add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, sa); 118add59d08SSam Leffler break; 119add59d08SSam Leffler case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ 120add59d08SSam Leffler break; 121add59d08SSam Leffler } 122add59d08SSam Leffler } else { 123add59d08SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 124add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, da); 125add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, sa); 126add59d08SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 127add59d08SSam Leffler } 128add59d08SSam Leffler *(u_int16_t *)&wh->i_dur[0] = 0; 129add59d08SSam Leffler /* NB: use non-QoS tid */ 130add59d08SSam Leffler *(u_int16_t *)&wh->i_seq[0] = 131add59d08SSam Leffler htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); 132add59d08SSam Leffler ni->ni_txseqs[0]++; 133add59d08SSam Leffler #undef WH4 134add59d08SSam Leffler } 135add59d08SSam Leffler 136add59d08SSam Leffler /* 1370a915fadSSam Leffler * Send a management frame to the specified node. The node pointer 1380a915fadSSam Leffler * must have a reference as the pointer will be passed to the driver 1390a915fadSSam Leffler * and potentially held for a long time. If the frame is successfully 1400a915fadSSam Leffler * dispatched to the driver, then it is responsible for freeing the 1410a915fadSSam Leffler * reference (and potentially free'ing up any associated storage). 1420a915fadSSam Leffler */ 1430a915fadSSam Leffler static int 1448a1b9b6aSSam Leffler ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, 1456d049ab3SSam Leffler struct mbuf *m, int type, int timer) 1461a1e1d21SSam Leffler { 1478a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1481a1e1d21SSam Leffler struct ieee80211_frame *wh; 1491a1e1d21SSam Leffler 1500a915fadSSam Leffler KASSERT(ni != NULL, ("null node")); 1511a1e1d21SSam Leffler 1520a915fadSSam Leffler /* 1530a915fadSSam Leffler * Yech, hack alert! We want to pass the node down to the 1540a915fadSSam Leffler * driver's start routine. If we don't do so then the start 1550a915fadSSam Leffler * routine must immediately look it up again and that can 1560a915fadSSam Leffler * cause a lock order reversal if, for example, this frame 1570a915fadSSam Leffler * is being sent because the station is being timedout and 1580a915fadSSam Leffler * the frame being sent is a DEAUTH message. We could stick 1590a915fadSSam Leffler * this in an m_tag and tack that on to the mbuf. However 1600a915fadSSam Leffler * that's rather expensive to do for every frame so instead 1610a915fadSSam Leffler * we stuff it in the rcvif field since outbound frames do 1620a915fadSSam Leffler * not (presently) use this. 1630a915fadSSam Leffler */ 1641a1e1d21SSam Leffler M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 1651a1e1d21SSam Leffler if (m == NULL) 1661a1e1d21SSam Leffler return ENOMEM; 1670a915fadSSam Leffler KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); 1680a915fadSSam Leffler m->m_pkthdr.rcvif = (void *)ni; 1690a915fadSSam Leffler 1701a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 171add59d08SSam Leffler ieee80211_send_setup(ic, ni, wh, 172add59d08SSam Leffler IEEE80211_FC0_TYPE_MGT | type, 173add59d08SSam Leffler ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); 1748a1b9b6aSSam Leffler if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { 1758a1b9b6aSSam Leffler m->m_flags &= ~M_LINK0; 1768a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 1778a1b9b6aSSam Leffler "[%s] encrypting frame (%s)\n", 1788a1b9b6aSSam Leffler ether_sprintf(wh->i_addr1), __func__); 1798a1b9b6aSSam Leffler wh->i_fc[1] |= IEEE80211_FC1_WEP; 1808a1b9b6aSSam Leffler } 1818a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 1828a1b9b6aSSam Leffler /* avoid printing too many frames */ 1838a1b9b6aSSam Leffler if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || 1848a1b9b6aSSam Leffler ieee80211_msg_dumppkts(ic)) { 1858a1b9b6aSSam Leffler printf("[%s] send %s on channel %u\n", 1868a1b9b6aSSam Leffler ether_sprintf(wh->i_addr1), 1878a1b9b6aSSam Leffler ieee80211_mgt_subtype_name[ 1888a1b9b6aSSam Leffler (type & IEEE80211_FC0_SUBTYPE_MASK) >> 1898a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_SHIFT], 190b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan)); 1918a1b9b6aSSam Leffler } 1928a1b9b6aSSam Leffler #endif 1938a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_mgmt); 1941a1e1d21SSam Leffler IF_ENQUEUE(&ic->ic_mgtq, m); 1956d049ab3SSam Leffler if (timer) { 1966d049ab3SSam Leffler /* 1976d049ab3SSam Leffler * Set the mgt frame timeout. 1986d049ab3SSam Leffler */ 1996d049ab3SSam Leffler ic->ic_mgt_timer = timer; 2001a1e1d21SSam Leffler ifp->if_timer = 1; 2016d049ab3SSam Leffler } 202af5e59bfSRobert Watson if_start(ifp); 2031a1e1d21SSam Leffler return 0; 2041a1e1d21SSam Leffler } 2051a1e1d21SSam Leffler 2060a915fadSSam Leffler /* 207246b5467SSam Leffler * Raw packet transmit stub for legacy drivers. 208246b5467SSam Leffler * Send the packet through the mgt q so we bypass 209246b5467SSam Leffler * the normal encapsulation work. 210246b5467SSam Leffler */ 211246b5467SSam Leffler int 212246b5467SSam Leffler ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 213246b5467SSam Leffler const struct ieee80211_bpf_params *params) 214246b5467SSam Leffler { 215246b5467SSam Leffler struct ieee80211com *ic = ni->ni_ic; 216246b5467SSam Leffler struct ifnet *ifp = ic->ic_ifp; 217246b5467SSam Leffler 218246b5467SSam Leffler m->m_pkthdr.rcvif = (void *) ni; 219246b5467SSam Leffler IF_ENQUEUE(&ic->ic_mgtq, m); 220246b5467SSam Leffler if_start(ifp); 221246b5467SSam Leffler ifp->if_opackets++; 222246b5467SSam Leffler 223246b5467SSam Leffler return 0; 224246b5467SSam Leffler } 225246b5467SSam Leffler 226246b5467SSam Leffler /* 227246b5467SSam Leffler * 802.11 output routine. This is (currently) used only to 228246b5467SSam Leffler * connect bpf write calls to the 802.11 layer for injecting 229246b5467SSam Leffler * raw 802.11 frames. Note we locate the ieee80211com from 230246b5467SSam Leffler * the ifnet using a spare field setup at attach time. This 231246b5467SSam Leffler * will go away when the virtual ap support comes in. 232246b5467SSam Leffler */ 233246b5467SSam Leffler int 234246b5467SSam Leffler ieee80211_output(struct ifnet *ifp, struct mbuf *m, 235246b5467SSam Leffler struct sockaddr *dst, struct rtentry *rt0) 236246b5467SSam Leffler { 237246b5467SSam Leffler #define senderr(e) do { error = (e); goto bad;} while (0) 238246b5467SSam Leffler struct ieee80211com *ic = ifp->if_spare2; /* XXX */ 239246b5467SSam Leffler struct ieee80211_node *ni = NULL; 240246b5467SSam Leffler struct ieee80211_frame *wh; 241246b5467SSam Leffler int error; 242246b5467SSam Leffler 243246b5467SSam Leffler /* 244246b5467SSam Leffler * Hand to the 802.3 code if not tagged as 245246b5467SSam Leffler * a raw 802.11 frame. 246246b5467SSam Leffler */ 247246b5467SSam Leffler if (dst->sa_family != AF_IEEE80211) 248246b5467SSam Leffler return ether_output(ifp, m, dst, rt0); 249246b5467SSam Leffler #ifdef MAC 250246b5467SSam Leffler error = mac_check_ifnet_transmit(ifp, m); 251246b5467SSam Leffler if (error) 252246b5467SSam Leffler senderr(error); 253246b5467SSam Leffler #endif 254246b5467SSam Leffler if (ifp->if_flags & IFF_MONITOR) 255246b5467SSam Leffler senderr(ENETDOWN); 256246b5467SSam Leffler if ((ifp->if_flags & IFF_UP) == 0) 257246b5467SSam Leffler senderr(ENETDOWN); 258246b5467SSam Leffler 259246b5467SSam Leffler /* XXX bypass bridge, pfil, carp, etc. */ 260246b5467SSam Leffler 261246b5467SSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) 262246b5467SSam Leffler senderr(EIO); /* XXX */ 263246b5467SSam Leffler wh = mtod(m, struct ieee80211_frame *); 264246b5467SSam Leffler if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 265246b5467SSam Leffler IEEE80211_FC0_VERSION_0) 266246b5467SSam Leffler senderr(EIO); /* XXX */ 267246b5467SSam Leffler 268246b5467SSam Leffler /* locate destination node */ 269246b5467SSam Leffler switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 270246b5467SSam Leffler case IEEE80211_FC1_DIR_NODS: 271246b5467SSam Leffler case IEEE80211_FC1_DIR_FROMDS: 272246b5467SSam Leffler ni = ieee80211_find_txnode(ic, wh->i_addr1); 273246b5467SSam Leffler break; 274246b5467SSam Leffler case IEEE80211_FC1_DIR_TODS: 275246b5467SSam Leffler case IEEE80211_FC1_DIR_DSTODS: 276246b5467SSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) 277246b5467SSam Leffler senderr(EIO); /* XXX */ 278246b5467SSam Leffler ni = ieee80211_find_txnode(ic, wh->i_addr3); 279246b5467SSam Leffler break; 280246b5467SSam Leffler default: 281246b5467SSam Leffler senderr(EIO); /* XXX */ 282246b5467SSam Leffler } 283246b5467SSam Leffler if (ni == NULL) { 284246b5467SSam Leffler /* 285246b5467SSam Leffler * Permit packets w/ bpf params through regardless 286246b5467SSam Leffler * (see below about sa_len). 287246b5467SSam Leffler */ 288246b5467SSam Leffler if (dst->sa_len == 0) 289246b5467SSam Leffler senderr(EHOSTUNREACH); 290246b5467SSam Leffler ni = ieee80211_ref_node(ic->ic_bss); 291246b5467SSam Leffler } 292246b5467SSam Leffler 293246b5467SSam Leffler /* XXX ctrl frames should go through */ 294246b5467SSam Leffler if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 295246b5467SSam Leffler (m->m_flags & M_PWR_SAV) == 0) { 296246b5467SSam Leffler /* 297246b5467SSam Leffler * Station in power save mode; pass the frame 298246b5467SSam Leffler * to the 802.11 layer and continue. We'll get 299246b5467SSam Leffler * the frame back when the time is right. 300246b5467SSam Leffler */ 301246b5467SSam Leffler ieee80211_pwrsave(ic, ni, m); 302246b5467SSam Leffler error = 0; 303246b5467SSam Leffler goto reclaim; 304246b5467SSam Leffler } 305246b5467SSam Leffler 306246b5467SSam Leffler /* calculate priority so drivers can find the tx queue */ 307246b5467SSam Leffler /* XXX assumes an 802.3 frame */ 308246b5467SSam Leffler if (ieee80211_classify(ic, m, ni)) 309246b5467SSam Leffler senderr(EIO); /* XXX */ 310246b5467SSam Leffler 311246b5467SSam Leffler BPF_MTAP(ifp, m); 312246b5467SSam Leffler /* 313246b5467SSam Leffler * NB: DLT_IEEE802_11_RADIO identifies the parameters are 314246b5467SSam Leffler * present by setting the sa_len field of the sockaddr (yes, 315246b5467SSam Leffler * this is a hack). 316246b5467SSam Leffler * NB: we assume sa_data is suitably aligned to cast. 317246b5467SSam Leffler */ 318246b5467SSam Leffler return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *) 319246b5467SSam Leffler (dst->sa_len ? dst->sa_data : NULL)); 320246b5467SSam Leffler bad: 321246b5467SSam Leffler if (m != NULL) 322246b5467SSam Leffler m_freem(m); 323246b5467SSam Leffler reclaim: 324246b5467SSam Leffler if (ni != NULL) 325246b5467SSam Leffler ieee80211_free_node(ni); 326246b5467SSam Leffler return error; 327246b5467SSam Leffler #undef senderr 328246b5467SSam Leffler } 329246b5467SSam Leffler 330246b5467SSam Leffler /* 3318a1b9b6aSSam Leffler * Send a null data frame to the specified node. 33219ad2dd7SSam Leffler * 33319ad2dd7SSam Leffler * NB: the caller is assumed to have setup a node reference 33419ad2dd7SSam Leffler * for use; this is necessary to deal with a race condition 33519ad2dd7SSam Leffler * when probing for inactive stations. 3368a1b9b6aSSam Leffler */ 3378a1b9b6aSSam Leffler int 338f62121ceSSam Leffler ieee80211_send_nulldata(struct ieee80211_node *ni) 3398a1b9b6aSSam Leffler { 340f62121ceSSam Leffler struct ieee80211com *ic = ni->ni_ic; 3418a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 3428a1b9b6aSSam Leffler struct mbuf *m; 3438a1b9b6aSSam Leffler struct ieee80211_frame *wh; 3448a1b9b6aSSam Leffler 34534333b16SAndre Oppermann MGETHDR(m, M_NOWAIT, MT_DATA); 3468a1b9b6aSSam Leffler if (m == NULL) { 3478a1b9b6aSSam Leffler /* XXX debug msg */ 3488a1b9b6aSSam Leffler ic->ic_stats.is_tx_nobuf++; 34919ad2dd7SSam Leffler ieee80211_unref_node(&ni); 3508a1b9b6aSSam Leffler return ENOMEM; 3518a1b9b6aSSam Leffler } 35219ad2dd7SSam Leffler m->m_pkthdr.rcvif = (void *) ni; 3538a1b9b6aSSam Leffler 3548a1b9b6aSSam Leffler wh = mtod(m, struct ieee80211_frame *); 355add59d08SSam Leffler ieee80211_send_setup(ic, ni, wh, 356add59d08SSam Leffler IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, 357add59d08SSam Leffler ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid); 358add59d08SSam Leffler /* NB: power management bit is never sent by an AP */ 359add59d08SSam Leffler if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 360add59d08SSam Leffler ic->ic_opmode != IEEE80211_M_HOSTAP) 361add59d08SSam Leffler wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; 3628a1b9b6aSSam Leffler m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame); 3638a1b9b6aSSam Leffler 3648a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_data); 3658a1b9b6aSSam Leffler 366add59d08SSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 367add59d08SSam Leffler "[%s] send null data frame on channel %u, pwr mgt %s\n", 368add59d08SSam Leffler ether_sprintf(ni->ni_macaddr), 369b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 370add59d08SSam Leffler wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); 371add59d08SSam Leffler 3728a1b9b6aSSam Leffler IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */ 3738a1b9b6aSSam Leffler if_start(ifp); 3748a1b9b6aSSam Leffler 3758a1b9b6aSSam Leffler return 0; 3768a1b9b6aSSam Leffler } 3778a1b9b6aSSam Leffler 3788a1b9b6aSSam Leffler /* 3798a1b9b6aSSam Leffler * Assign priority to a frame based on any vlan tag assigned 3808a1b9b6aSSam Leffler * to the station and/or any Diffserv setting in an IP header. 3818a1b9b6aSSam Leffler * Finally, if an ACM policy is setup (in station mode) it's 3828a1b9b6aSSam Leffler * applied. 3838a1b9b6aSSam Leffler */ 3848a1b9b6aSSam Leffler int 3858a1b9b6aSSam Leffler ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni) 3868a1b9b6aSSam Leffler { 3878a1b9b6aSSam Leffler int v_wme_ac, d_wme_ac, ac; 3888a1b9b6aSSam Leffler #ifdef INET 3898a1b9b6aSSam Leffler struct ether_header *eh; 3908a1b9b6aSSam Leffler #endif 3918a1b9b6aSSam Leffler 3928a1b9b6aSSam Leffler if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { 3938a1b9b6aSSam Leffler ac = WME_AC_BE; 3948a1b9b6aSSam Leffler goto done; 3958a1b9b6aSSam Leffler } 3968a1b9b6aSSam Leffler 3978a1b9b6aSSam Leffler /* 3988a1b9b6aSSam Leffler * If node has a vlan tag then all traffic 3998a1b9b6aSSam Leffler * to it must have a matching tag. 4008a1b9b6aSSam Leffler */ 4018a1b9b6aSSam Leffler v_wme_ac = 0; 4028a1b9b6aSSam Leffler if (ni->ni_vlan != 0) { 40378ba57b9SAndre Oppermann if ((m->m_flags & M_VLANTAG) == 0) { 4048a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_novlantag); 4058a1b9b6aSSam Leffler return 1; 4068a1b9b6aSSam Leffler } 40778ba57b9SAndre Oppermann if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != 4088a1b9b6aSSam Leffler EVL_VLANOFTAG(ni->ni_vlan)) { 4098a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_vlanmismatch); 4108a1b9b6aSSam Leffler return 1; 4118a1b9b6aSSam Leffler } 4128a1b9b6aSSam Leffler /* map vlan priority to AC */ 4138a1b9b6aSSam Leffler switch (EVL_PRIOFTAG(ni->ni_vlan)) { 4148a1b9b6aSSam Leffler case 1: 4158a1b9b6aSSam Leffler case 2: 4168a1b9b6aSSam Leffler v_wme_ac = WME_AC_BK; 4178a1b9b6aSSam Leffler break; 4188a1b9b6aSSam Leffler case 0: 4198a1b9b6aSSam Leffler case 3: 4208a1b9b6aSSam Leffler v_wme_ac = WME_AC_BE; 4218a1b9b6aSSam Leffler break; 4228a1b9b6aSSam Leffler case 4: 4238a1b9b6aSSam Leffler case 5: 4248a1b9b6aSSam Leffler v_wme_ac = WME_AC_VI; 4258a1b9b6aSSam Leffler break; 4268a1b9b6aSSam Leffler case 6: 4278a1b9b6aSSam Leffler case 7: 4288a1b9b6aSSam Leffler v_wme_ac = WME_AC_VO; 4298a1b9b6aSSam Leffler break; 4308a1b9b6aSSam Leffler } 4318a1b9b6aSSam Leffler } 4328a1b9b6aSSam Leffler 4338a1b9b6aSSam Leffler #ifdef INET 4348a1b9b6aSSam Leffler eh = mtod(m, struct ether_header *); 4358a1b9b6aSSam Leffler if (eh->ether_type == htons(ETHERTYPE_IP)) { 4368a1b9b6aSSam Leffler const struct ip *ip = (struct ip *) 4378a1b9b6aSSam Leffler (mtod(m, u_int8_t *) + sizeof (*eh)); 4388a1b9b6aSSam Leffler /* 4398a1b9b6aSSam Leffler * IP frame, map the TOS field. 4408a1b9b6aSSam Leffler */ 4418a1b9b6aSSam Leffler switch (ip->ip_tos) { 4428a1b9b6aSSam Leffler case 0x08: 4438a1b9b6aSSam Leffler case 0x20: 4448a1b9b6aSSam Leffler d_wme_ac = WME_AC_BK; /* background */ 4458a1b9b6aSSam Leffler break; 4468a1b9b6aSSam Leffler case 0x28: 4478a1b9b6aSSam Leffler case 0xa0: 4488a1b9b6aSSam Leffler d_wme_ac = WME_AC_VI; /* video */ 4498a1b9b6aSSam Leffler break; 4508a1b9b6aSSam Leffler case 0x30: /* voice */ 4518a1b9b6aSSam Leffler case 0xe0: 4528a1b9b6aSSam Leffler case 0x88: /* XXX UPSD */ 4538a1b9b6aSSam Leffler case 0xb8: 4548a1b9b6aSSam Leffler d_wme_ac = WME_AC_VO; 4558a1b9b6aSSam Leffler break; 4568a1b9b6aSSam Leffler default: 4578a1b9b6aSSam Leffler d_wme_ac = WME_AC_BE; 4588a1b9b6aSSam Leffler break; 4598a1b9b6aSSam Leffler } 4608a1b9b6aSSam Leffler } else { 4618a1b9b6aSSam Leffler #endif /* INET */ 4628a1b9b6aSSam Leffler d_wme_ac = WME_AC_BE; 4638a1b9b6aSSam Leffler #ifdef INET 4648a1b9b6aSSam Leffler } 4658a1b9b6aSSam Leffler #endif 4668a1b9b6aSSam Leffler /* 4678a1b9b6aSSam Leffler * Use highest priority AC. 4688a1b9b6aSSam Leffler */ 4698a1b9b6aSSam Leffler if (v_wme_ac > d_wme_ac) 4708a1b9b6aSSam Leffler ac = v_wme_ac; 4718a1b9b6aSSam Leffler else 4728a1b9b6aSSam Leffler ac = d_wme_ac; 4738a1b9b6aSSam Leffler 4748a1b9b6aSSam Leffler /* 4758a1b9b6aSSam Leffler * Apply ACM policy. 4768a1b9b6aSSam Leffler */ 4778a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 4788a1b9b6aSSam Leffler static const int acmap[4] = { 4798a1b9b6aSSam Leffler WME_AC_BK, /* WME_AC_BE */ 4808a1b9b6aSSam Leffler WME_AC_BK, /* WME_AC_BK */ 4818a1b9b6aSSam Leffler WME_AC_BE, /* WME_AC_VI */ 4828a1b9b6aSSam Leffler WME_AC_VI, /* WME_AC_VO */ 4838a1b9b6aSSam Leffler }; 4848a1b9b6aSSam Leffler while (ac != WME_AC_BK && 4858a1b9b6aSSam Leffler ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) 4868a1b9b6aSSam Leffler ac = acmap[ac]; 4878a1b9b6aSSam Leffler } 4888a1b9b6aSSam Leffler done: 4898a1b9b6aSSam Leffler M_WME_SETAC(m, ac); 4908a1b9b6aSSam Leffler return 0; 4918a1b9b6aSSam Leffler } 4928a1b9b6aSSam Leffler 4938a1b9b6aSSam Leffler /* 4945e923d2eSSam Leffler * Insure there is sufficient contiguous space to encapsulate the 4955e923d2eSSam Leffler * 802.11 data frame. If room isn't already there, arrange for it. 4965e923d2eSSam Leffler * Drivers and cipher modules assume we have done the necessary work 4975e923d2eSSam Leffler * and fail rudely if they don't find the space they need. 4985e923d2eSSam Leffler */ 4995e923d2eSSam Leffler static struct mbuf * 5005e923d2eSSam Leffler ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize, 5015e923d2eSSam Leffler struct ieee80211_key *key, struct mbuf *m) 5025e923d2eSSam Leffler { 5035e923d2eSSam Leffler #define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) 5045e923d2eSSam Leffler int needed_space = hdrsize; 5055e923d2eSSam Leffler 5065e923d2eSSam Leffler if (key != NULL) { 5075e923d2eSSam Leffler /* XXX belongs in crypto code? */ 5085e923d2eSSam Leffler needed_space += key->wk_cipher->ic_header; 5095e923d2eSSam Leffler /* XXX frags */ 51083a244dbSSam Leffler /* 51183a244dbSSam Leffler * When crypto is being done in the host we must insure 51283a244dbSSam Leffler * the data are writable for the cipher routines; clone 51383a244dbSSam Leffler * a writable mbuf chain. 51483a244dbSSam Leffler * XXX handle SWMIC specially 51583a244dbSSam Leffler */ 51683a244dbSSam Leffler if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) { 51783a244dbSSam Leffler m = m_unshare(m, M_NOWAIT); 51883a244dbSSam Leffler if (m == NULL) { 51983a244dbSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, 52083a244dbSSam Leffler "%s: cannot get writable mbuf\n", __func__); 52183a244dbSSam Leffler ic->ic_stats.is_tx_nobuf++; /* XXX new stat */ 52283a244dbSSam Leffler return NULL; 52383a244dbSSam Leffler } 52483a244dbSSam Leffler } 5255e923d2eSSam Leffler } 5265e923d2eSSam Leffler /* 5275e923d2eSSam Leffler * We know we are called just before stripping an Ethernet 5285e923d2eSSam Leffler * header and prepending an LLC header. This means we know 5295e923d2eSSam Leffler * there will be 5305e923d2eSSam Leffler * sizeof(struct ether_header) - sizeof(struct llc) 5315e923d2eSSam Leffler * bytes recovered to which we need additional space for the 5325e923d2eSSam Leffler * 802.11 header and any crypto header. 5335e923d2eSSam Leffler */ 5345e923d2eSSam Leffler /* XXX check trailing space and copy instead? */ 5355e923d2eSSam Leffler if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { 5365e923d2eSSam Leffler struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); 5375e923d2eSSam Leffler if (n == NULL) { 5385e923d2eSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, 5395e923d2eSSam Leffler "%s: cannot expand storage\n", __func__); 5405e923d2eSSam Leffler ic->ic_stats.is_tx_nobuf++; 5415e923d2eSSam Leffler m_freem(m); 5425e923d2eSSam Leffler return NULL; 5435e923d2eSSam Leffler } 5445e923d2eSSam Leffler KASSERT(needed_space <= MHLEN, 5455e923d2eSSam Leffler ("not enough room, need %u got %zu\n", needed_space, MHLEN)); 5465e923d2eSSam Leffler /* 5475e923d2eSSam Leffler * Setup new mbuf to have leading space to prepend the 5485e923d2eSSam Leffler * 802.11 header and any crypto header bits that are 5495e923d2eSSam Leffler * required (the latter are added when the driver calls 5505e923d2eSSam Leffler * back to ieee80211_crypto_encap to do crypto encapsulation). 5515e923d2eSSam Leffler */ 5525e923d2eSSam Leffler /* NB: must be first 'cuz it clobbers m_data */ 5535e923d2eSSam Leffler m_move_pkthdr(n, m); 5545e923d2eSSam Leffler n->m_len = 0; /* NB: m_gethdr does not set */ 5555e923d2eSSam Leffler n->m_data += needed_space; 5565e923d2eSSam Leffler /* 5575e923d2eSSam Leffler * Pull up Ethernet header to create the expected layout. 5585e923d2eSSam Leffler * We could use m_pullup but that's overkill (i.e. we don't 5595e923d2eSSam Leffler * need the actual data) and it cannot fail so do it inline 5605e923d2eSSam Leffler * for speed. 5615e923d2eSSam Leffler */ 5625e923d2eSSam Leffler /* NB: struct ether_header is known to be contiguous */ 5635e923d2eSSam Leffler n->m_len += sizeof(struct ether_header); 5645e923d2eSSam Leffler m->m_len -= sizeof(struct ether_header); 5655e923d2eSSam Leffler m->m_data += sizeof(struct ether_header); 5665e923d2eSSam Leffler /* 5675e923d2eSSam Leffler * Replace the head of the chain. 5685e923d2eSSam Leffler */ 5695e923d2eSSam Leffler n->m_next = m; 5705e923d2eSSam Leffler m = n; 5715e923d2eSSam Leffler } 5725e923d2eSSam Leffler return m; 5735e923d2eSSam Leffler #undef TO_BE_RECLAIMED 5745e923d2eSSam Leffler } 5755e923d2eSSam Leffler 5765e923d2eSSam Leffler #define KEY_UNDEFINED(k) ((k).wk_cipher == &ieee80211_cipher_none) 5775e923d2eSSam Leffler /* 5785e923d2eSSam Leffler * Return the transmit key to use in sending a unicast frame. 5795e923d2eSSam Leffler * If a unicast key is set we use that. When no unicast key is set 5805e923d2eSSam Leffler * we fall back to the default transmit key. 5818a1b9b6aSSam Leffler */ 5828a1b9b6aSSam Leffler static __inline struct ieee80211_key * 5835e923d2eSSam Leffler ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni) 5848a1b9b6aSSam Leffler { 5855e923d2eSSam Leffler if (KEY_UNDEFINED(ni->ni_ucastkey)) { 5868a1b9b6aSSam Leffler if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || 5878a1b9b6aSSam Leffler KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) 5888a1b9b6aSSam Leffler return NULL; 5898a1b9b6aSSam Leffler return &ic->ic_nw_keys[ic->ic_def_txkey]; 5908a1b9b6aSSam Leffler } else { 5918a1b9b6aSSam Leffler return &ni->ni_ucastkey; 5928a1b9b6aSSam Leffler } 5935e923d2eSSam Leffler } 5945e923d2eSSam Leffler 5955e923d2eSSam Leffler /* 5965e923d2eSSam Leffler * Return the transmit key to use in sending a multicast frame. 5975e923d2eSSam Leffler * Multicast traffic always uses the group key which is installed as 5985e923d2eSSam Leffler * the default tx key. 5995e923d2eSSam Leffler */ 6005e923d2eSSam Leffler static __inline struct ieee80211_key * 6015e923d2eSSam Leffler ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni) 6025e923d2eSSam Leffler { 6035e923d2eSSam Leffler if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || 6045e923d2eSSam Leffler KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) 6055e923d2eSSam Leffler return NULL; 6065e923d2eSSam Leffler return &ic->ic_nw_keys[ic->ic_def_txkey]; 6078a1b9b6aSSam Leffler } 6088a1b9b6aSSam Leffler 6098a1b9b6aSSam Leffler /* 6108a1b9b6aSSam Leffler * Encapsulate an outbound data frame. The mbuf chain is updated. 6118a1b9b6aSSam Leffler * If an error is encountered NULL is returned. The caller is required 6128a1b9b6aSSam Leffler * to provide a node reference and pullup the ethernet header in the 6138a1b9b6aSSam Leffler * first mbuf. 6140a915fadSSam Leffler */ 6151a1e1d21SSam Leffler struct mbuf * 6168a1b9b6aSSam Leffler ieee80211_encap(struct ieee80211com *ic, struct mbuf *m, 6178a1b9b6aSSam Leffler struct ieee80211_node *ni) 6181a1e1d21SSam Leffler { 6191a1e1d21SSam Leffler struct ether_header eh; 6201a1e1d21SSam Leffler struct ieee80211_frame *wh; 6218a1b9b6aSSam Leffler struct ieee80211_key *key; 6221a1e1d21SSam Leffler struct llc *llc; 6235e923d2eSSam Leffler int hdrsize, datalen, addqos; 6241a1e1d21SSam Leffler 6258a1b9b6aSSam Leffler KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); 6261a1e1d21SSam Leffler memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); 6271a1e1d21SSam Leffler 6288a1b9b6aSSam Leffler /* 6298a1b9b6aSSam Leffler * Insure space for additional headers. First identify 6308a1b9b6aSSam Leffler * transmit key to use in calculating any buffer adjustments 6318a1b9b6aSSam Leffler * required. This is also used below to do privacy 6328a1b9b6aSSam Leffler * encapsulation work. Then calculate the 802.11 header 6338a1b9b6aSSam Leffler * size and any padding required by the driver. 6348a1b9b6aSSam Leffler * 6358a1b9b6aSSam Leffler * Note key may be NULL if we fall back to the default 6368a1b9b6aSSam Leffler * transmit key and that is not set. In that case the 6378a1b9b6aSSam Leffler * buffer may not be expanded as needed by the cipher 6388a1b9b6aSSam Leffler * routines, but they will/should discard it. 6398a1b9b6aSSam Leffler */ 6408a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_PRIVACY) { 6415e923d2eSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA || 6425e923d2eSSam Leffler !IEEE80211_IS_MULTICAST(eh.ether_dhost)) 6435e923d2eSSam Leffler key = ieee80211_crypto_getucastkey(ic, ni); 6445e923d2eSSam Leffler else 6455e923d2eSSam Leffler key = ieee80211_crypto_getmcastkey(ic, ni); 6468a1b9b6aSSam Leffler if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) { 6478a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, 6485e923d2eSSam Leffler "[%s] no default transmit key (%s) deftxkey %u\n", 6495e923d2eSSam Leffler ether_sprintf(eh.ether_dhost), __func__, 6505e923d2eSSam Leffler ic->ic_def_txkey); 6515e923d2eSSam Leffler ic->ic_stats.is_tx_nodefkey++; 6528a1b9b6aSSam Leffler } 6538a1b9b6aSSam Leffler } else 6548a1b9b6aSSam Leffler key = NULL; 6558a1b9b6aSSam Leffler /* XXX 4-address format */ 6565e923d2eSSam Leffler /* 6575e923d2eSSam Leffler * XXX Some ap's don't handle QoS-encapsulated EAPOL 6585e923d2eSSam Leffler * frames so suppress use. This may be an issue if other 6595e923d2eSSam Leffler * ap's require all data frames to be QoS-encapsulated 6605e923d2eSSam Leffler * once negotiated in which case we'll need to make this 6615e923d2eSSam Leffler * configurable. 6625e923d2eSSam Leffler */ 6635e923d2eSSam Leffler addqos = (ni->ni_flags & IEEE80211_NODE_QOS) && 6645e923d2eSSam Leffler eh.ether_type != htons(ETHERTYPE_PAE); 6655e923d2eSSam Leffler if (addqos) 6668a1b9b6aSSam Leffler hdrsize = sizeof(struct ieee80211_qosframe); 6678a1b9b6aSSam Leffler else 6688a1b9b6aSSam Leffler hdrsize = sizeof(struct ieee80211_frame); 6698a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_DATAPAD) 6708a1b9b6aSSam Leffler hdrsize = roundup(hdrsize, sizeof(u_int32_t)); 6718a1b9b6aSSam Leffler m = ieee80211_mbuf_adjust(ic, hdrsize, key, m); 6728a1b9b6aSSam Leffler if (m == NULL) { 6738a1b9b6aSSam Leffler /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ 6740a915fadSSam Leffler goto bad; 6750a915fadSSam Leffler } 6761a1e1d21SSam Leffler 6778a1b9b6aSSam Leffler /* NB: this could be optimized because of ieee80211_mbuf_adjust */ 6781a1e1d21SSam Leffler m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 6791a1e1d21SSam Leffler llc = mtod(m, struct llc *); 6801a1e1d21SSam Leffler llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 6811a1e1d21SSam Leffler llc->llc_control = LLC_UI; 6821a1e1d21SSam Leffler llc->llc_snap.org_code[0] = 0; 6831a1e1d21SSam Leffler llc->llc_snap.org_code[1] = 0; 6841a1e1d21SSam Leffler llc->llc_snap.org_code[2] = 0; 6851a1e1d21SSam Leffler llc->llc_snap.ether_type = eh.ether_type; 6868a1b9b6aSSam Leffler datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ 6878a1b9b6aSSam Leffler 6888a1b9b6aSSam Leffler M_PREPEND(m, hdrsize, M_DONTWAIT); 6891be50176SSam Leffler if (m == NULL) { 6908a1b9b6aSSam Leffler ic->ic_stats.is_tx_nobuf++; 6910a915fadSSam Leffler goto bad; 6921be50176SSam Leffler } 6931a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 6941a1e1d21SSam Leffler wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 6951a1e1d21SSam Leffler *(u_int16_t *)wh->i_dur = 0; 6961a1e1d21SSam Leffler switch (ic->ic_opmode) { 6971a1e1d21SSam Leffler case IEEE80211_M_STA: 6981a1e1d21SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 6991a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); 7001a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 7011a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 7021a1e1d21SSam Leffler break; 7031a1e1d21SSam Leffler case IEEE80211_M_IBSS: 7041a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 7051a1e1d21SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 7061a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 7071a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 708a8b16e87SSam Leffler /* 709a8b16e87SSam Leffler * NB: always use the bssid from ic_bss as the 710a8b16e87SSam Leffler * neighbor's may be stale after an ibss merge 711a8b16e87SSam Leffler */ 712a8b16e87SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid); 7131a1e1d21SSam Leffler break; 7141a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 7151a1e1d21SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 7161a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 7171a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); 7181a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 7191a1e1d21SSam Leffler break; 7202bbe529dSSam Leffler case IEEE80211_M_MONITOR: 7210a915fadSSam Leffler goto bad; 7221a1e1d21SSam Leffler } 723bc5627d9SSam Leffler if (m->m_flags & M_MORE_DATA) 724bc5627d9SSam Leffler wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 7255e923d2eSSam Leffler if (addqos) { 7268a1b9b6aSSam Leffler struct ieee80211_qosframe *qwh = 7278a1b9b6aSSam Leffler (struct ieee80211_qosframe *) wh; 7288a1b9b6aSSam Leffler int ac, tid; 7298a1b9b6aSSam Leffler 7308a1b9b6aSSam Leffler ac = M_WME_GETAC(m); 7318a1b9b6aSSam Leffler /* map from access class/queue to 11e header priorty value */ 7328a1b9b6aSSam Leffler tid = WME_AC_TO_TID(ac); 7338a1b9b6aSSam Leffler qwh->i_qos[0] = tid & IEEE80211_QOS_TID; 7348a1b9b6aSSam Leffler if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) 7358a1b9b6aSSam Leffler qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S; 7368a1b9b6aSSam Leffler qwh->i_qos[1] = 0; 7378a1b9b6aSSam Leffler qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; 7388a1b9b6aSSam Leffler 7398a1b9b6aSSam Leffler *(u_int16_t *)wh->i_seq = 7408a1b9b6aSSam Leffler htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); 7418a1b9b6aSSam Leffler ni->ni_txseqs[tid]++; 7428a1b9b6aSSam Leffler } else { 7438a1b9b6aSSam Leffler *(u_int16_t *)wh->i_seq = 7448a1b9b6aSSam Leffler htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); 7458a1b9b6aSSam Leffler ni->ni_txseqs[0]++; 7468a1b9b6aSSam Leffler } 7475e923d2eSSam Leffler if (key != NULL) { 7485e923d2eSSam Leffler /* 7495e923d2eSSam Leffler * IEEE 802.1X: send EAPOL frames always in the clear. 7505e923d2eSSam Leffler * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. 7515e923d2eSSam Leffler */ 7525e923d2eSSam Leffler if (eh.ether_type != htons(ETHERTYPE_PAE) || 7535e923d2eSSam Leffler ((ic->ic_flags & IEEE80211_F_WPA) && 754b4b64678SSam Leffler (ic->ic_opmode == IEEE80211_M_STA ? 755b4b64678SSam Leffler !KEY_UNDEFINED(*key) : !KEY_UNDEFINED(ni->ni_ucastkey)))) { 7565e923d2eSSam Leffler wh->i_fc[1] |= IEEE80211_FC1_WEP; 7575e923d2eSSam Leffler /* XXX do fragmentation */ 75896d88463SSam Leffler if (!ieee80211_crypto_enmic(ic, key, m, 0)) { 7595e923d2eSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, 7605e923d2eSSam Leffler "[%s] enmic failed, discard frame\n", 7615e923d2eSSam Leffler ether_sprintf(eh.ether_dhost)); 7625e923d2eSSam Leffler ic->ic_stats.is_crypto_enmicfail++; 7635e923d2eSSam Leffler goto bad; 7645e923d2eSSam Leffler } 7655e923d2eSSam Leffler } 7665e923d2eSSam Leffler } 7678a1b9b6aSSam Leffler 7688a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_data); 769b6e9b119SSam Leffler if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 770b6e9b119SSam Leffler IEEE80211_NODE_STAT(ni, tx_mcast); 771b6e9b119SSam Leffler else 772b6e9b119SSam Leffler IEEE80211_NODE_STAT(ni, tx_ucast); 7738a1b9b6aSSam Leffler IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); 7748a1b9b6aSSam Leffler 7751a1e1d21SSam Leffler return m; 7760a915fadSSam Leffler bad: 7770a915fadSSam Leffler if (m != NULL) 7780a915fadSSam Leffler m_freem(m); 7790a915fadSSam Leffler return NULL; 7801a1e1d21SSam Leffler } 7811a1e1d21SSam Leffler 7821a1e1d21SSam Leffler /* 7831a1e1d21SSam Leffler * Add a supported rates element id to a frame. 7841a1e1d21SSam Leffler */ 7858a1b9b6aSSam Leffler static u_int8_t * 7861a1e1d21SSam Leffler ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) 7871a1e1d21SSam Leffler { 7881a1e1d21SSam Leffler int nrates; 7891a1e1d21SSam Leffler 7901a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_RATES; 7911a1e1d21SSam Leffler nrates = rs->rs_nrates; 7921a1e1d21SSam Leffler if (nrates > IEEE80211_RATE_SIZE) 7931a1e1d21SSam Leffler nrates = IEEE80211_RATE_SIZE; 7941a1e1d21SSam Leffler *frm++ = nrates; 7951a1e1d21SSam Leffler memcpy(frm, rs->rs_rates, nrates); 7961a1e1d21SSam Leffler return frm + nrates; 7971a1e1d21SSam Leffler } 7981a1e1d21SSam Leffler 7991a1e1d21SSam Leffler /* 8001a1e1d21SSam Leffler * Add an extended supported rates element id to a frame. 8011a1e1d21SSam Leffler */ 8028a1b9b6aSSam Leffler static u_int8_t * 8031a1e1d21SSam Leffler ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) 8041a1e1d21SSam Leffler { 8051a1e1d21SSam Leffler /* 8061a1e1d21SSam Leffler * Add an extended supported rates element if operating in 11g mode. 8071a1e1d21SSam Leffler */ 8081a1e1d21SSam Leffler if (rs->rs_nrates > IEEE80211_RATE_SIZE) { 8091a1e1d21SSam Leffler int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 8101a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_XRATES; 8111a1e1d21SSam Leffler *frm++ = nrates; 8121a1e1d21SSam Leffler memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 8131a1e1d21SSam Leffler frm += nrates; 8141a1e1d21SSam Leffler } 8151a1e1d21SSam Leffler return frm; 8161a1e1d21SSam Leffler } 8171a1e1d21SSam Leffler 8181a1e1d21SSam Leffler /* 8191a1e1d21SSam Leffler * Add an ssid elemet to a frame. 8201a1e1d21SSam Leffler */ 8211a1e1d21SSam Leffler static u_int8_t * 8221a1e1d21SSam Leffler ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) 8231a1e1d21SSam Leffler { 8241a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_SSID; 8251a1e1d21SSam Leffler *frm++ = len; 8261a1e1d21SSam Leffler memcpy(frm, ssid, len); 8271a1e1d21SSam Leffler return frm + len; 8281a1e1d21SSam Leffler } 8291a1e1d21SSam Leffler 8308a1b9b6aSSam Leffler /* 8318a1b9b6aSSam Leffler * Add an erp element to a frame. 8328a1b9b6aSSam Leffler */ 8338a1b9b6aSSam Leffler static u_int8_t * 8348a1b9b6aSSam Leffler ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic) 8351a1e1d21SSam Leffler { 8368a1b9b6aSSam Leffler u_int8_t erp; 8371a1e1d21SSam Leffler 8388a1b9b6aSSam Leffler *frm++ = IEEE80211_ELEMID_ERP; 8398a1b9b6aSSam Leffler *frm++ = 1; 8408a1b9b6aSSam Leffler erp = 0; 8418a1b9b6aSSam Leffler if (ic->ic_nonerpsta != 0) 8428a1b9b6aSSam Leffler erp |= IEEE80211_ERP_NON_ERP_PRESENT; 8438a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_USEPROT) 8448a1b9b6aSSam Leffler erp |= IEEE80211_ERP_USE_PROTECTION; 8458a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_USEBARKER) 8468a1b9b6aSSam Leffler erp |= IEEE80211_ERP_LONG_PREAMBLE; 8478a1b9b6aSSam Leffler *frm++ = erp; 8488a1b9b6aSSam Leffler return frm; 8491a1e1d21SSam Leffler } 8501a1e1d21SSam Leffler 8518a1b9b6aSSam Leffler static u_int8_t * 8528a1b9b6aSSam Leffler ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie) 8538a1b9b6aSSam Leffler { 8548a1b9b6aSSam Leffler #define WPA_OUI_BYTES 0x00, 0x50, 0xf2 8558a1b9b6aSSam Leffler #define ADDSHORT(frm, v) do { \ 8568a1b9b6aSSam Leffler frm[0] = (v) & 0xff; \ 8578a1b9b6aSSam Leffler frm[1] = (v) >> 8; \ 8588a1b9b6aSSam Leffler frm += 2; \ 8598a1b9b6aSSam Leffler } while (0) 8608a1b9b6aSSam Leffler #define ADDSELECTOR(frm, sel) do { \ 8618a1b9b6aSSam Leffler memcpy(frm, sel, 4); \ 8628a1b9b6aSSam Leffler frm += 4; \ 8638a1b9b6aSSam Leffler } while (0) 8648a1b9b6aSSam Leffler static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; 8658a1b9b6aSSam Leffler static const u_int8_t cipher_suite[][4] = { 8668a1b9b6aSSam Leffler { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */ 8678a1b9b6aSSam Leffler { WPA_OUI_BYTES, WPA_CSE_TKIP }, 8688a1b9b6aSSam Leffler { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */ 8698a1b9b6aSSam Leffler { WPA_OUI_BYTES, WPA_CSE_CCMP }, 8708a1b9b6aSSam Leffler { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ 8718a1b9b6aSSam Leffler { WPA_OUI_BYTES, WPA_CSE_NULL }, 8728a1b9b6aSSam Leffler }; 8738a1b9b6aSSam Leffler static const u_int8_t wep104_suite[4] = 8748a1b9b6aSSam Leffler { WPA_OUI_BYTES, WPA_CSE_WEP104 }; 8758a1b9b6aSSam Leffler static const u_int8_t key_mgt_unspec[4] = 8768a1b9b6aSSam Leffler { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC }; 8778a1b9b6aSSam Leffler static const u_int8_t key_mgt_psk[4] = 8788a1b9b6aSSam Leffler { WPA_OUI_BYTES, WPA_ASE_8021X_PSK }; 8798a1b9b6aSSam Leffler const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 8808a1b9b6aSSam Leffler u_int8_t *frm = ie; 8818a1b9b6aSSam Leffler u_int8_t *selcnt; 8828a1b9b6aSSam Leffler 8838a1b9b6aSSam Leffler *frm++ = IEEE80211_ELEMID_VENDOR; 8848a1b9b6aSSam Leffler *frm++ = 0; /* length filled in below */ 8858a1b9b6aSSam Leffler memcpy(frm, oui, sizeof(oui)); /* WPA OUI */ 8868a1b9b6aSSam Leffler frm += sizeof(oui); 8878a1b9b6aSSam Leffler ADDSHORT(frm, WPA_VERSION); 8888a1b9b6aSSam Leffler 8898a1b9b6aSSam Leffler /* XXX filter out CKIP */ 8908a1b9b6aSSam Leffler 8918a1b9b6aSSam Leffler /* multicast cipher */ 8928a1b9b6aSSam Leffler if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && 8938a1b9b6aSSam Leffler rsn->rsn_mcastkeylen >= 13) 8948a1b9b6aSSam Leffler ADDSELECTOR(frm, wep104_suite); 8958a1b9b6aSSam Leffler else 8968a1b9b6aSSam Leffler ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); 8978a1b9b6aSSam Leffler 8988a1b9b6aSSam Leffler /* unicast cipher list */ 8998a1b9b6aSSam Leffler selcnt = frm; 9008a1b9b6aSSam Leffler ADDSHORT(frm, 0); /* selector count */ 9018a1b9b6aSSam Leffler if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) { 9028a1b9b6aSSam Leffler selcnt[0]++; 9038a1b9b6aSSam Leffler ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); 9048a1b9b6aSSam Leffler } 9058a1b9b6aSSam Leffler if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { 9068a1b9b6aSSam Leffler selcnt[0]++; 9078a1b9b6aSSam Leffler ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); 9088a1b9b6aSSam Leffler } 9098a1b9b6aSSam Leffler 9108a1b9b6aSSam Leffler /* authenticator selector list */ 9118a1b9b6aSSam Leffler selcnt = frm; 9128a1b9b6aSSam Leffler ADDSHORT(frm, 0); /* selector count */ 9138a1b9b6aSSam Leffler if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { 9148a1b9b6aSSam Leffler selcnt[0]++; 9158a1b9b6aSSam Leffler ADDSELECTOR(frm, key_mgt_unspec); 9168a1b9b6aSSam Leffler } 9178a1b9b6aSSam Leffler if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { 9188a1b9b6aSSam Leffler selcnt[0]++; 9198a1b9b6aSSam Leffler ADDSELECTOR(frm, key_mgt_psk); 9208a1b9b6aSSam Leffler } 9218a1b9b6aSSam Leffler 9228a1b9b6aSSam Leffler /* optional capabilities */ 9236aa57182SSam Leffler if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH) 9248a1b9b6aSSam Leffler ADDSHORT(frm, rsn->rsn_caps); 9258a1b9b6aSSam Leffler 9268a1b9b6aSSam Leffler /* calculate element length */ 9278a1b9b6aSSam Leffler ie[1] = frm - ie - 2; 9288a1b9b6aSSam Leffler KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), 92916574882SSam Leffler ("WPA IE too big, %u > %zu", 9308a1b9b6aSSam Leffler ie[1]+2, sizeof(struct ieee80211_ie_wpa))); 9318a1b9b6aSSam Leffler return frm; 9328a1b9b6aSSam Leffler #undef ADDSHORT 9338a1b9b6aSSam Leffler #undef ADDSELECTOR 9348a1b9b6aSSam Leffler #undef WPA_OUI_BYTES 9358a1b9b6aSSam Leffler } 9368a1b9b6aSSam Leffler 9378a1b9b6aSSam Leffler static u_int8_t * 9388a1b9b6aSSam Leffler ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie) 9398a1b9b6aSSam Leffler { 9408a1b9b6aSSam Leffler #define RSN_OUI_BYTES 0x00, 0x0f, 0xac 9418a1b9b6aSSam Leffler #define ADDSHORT(frm, v) do { \ 9428a1b9b6aSSam Leffler frm[0] = (v) & 0xff; \ 9438a1b9b6aSSam Leffler frm[1] = (v) >> 8; \ 9448a1b9b6aSSam Leffler frm += 2; \ 9458a1b9b6aSSam Leffler } while (0) 9468a1b9b6aSSam Leffler #define ADDSELECTOR(frm, sel) do { \ 9478a1b9b6aSSam Leffler memcpy(frm, sel, 4); \ 9488a1b9b6aSSam Leffler frm += 4; \ 9498a1b9b6aSSam Leffler } while (0) 9508a1b9b6aSSam Leffler static const u_int8_t cipher_suite[][4] = { 9518a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */ 9528a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_CSE_TKIP }, 9538a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_CSE_WRAP }, 9548a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_CSE_CCMP }, 9558a1b9b6aSSam Leffler { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ 9568a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_CSE_NULL }, 9578a1b9b6aSSam Leffler }; 9588a1b9b6aSSam Leffler static const u_int8_t wep104_suite[4] = 9598a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_CSE_WEP104 }; 9608a1b9b6aSSam Leffler static const u_int8_t key_mgt_unspec[4] = 9618a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC }; 9628a1b9b6aSSam Leffler static const u_int8_t key_mgt_psk[4] = 9638a1b9b6aSSam Leffler { RSN_OUI_BYTES, RSN_ASE_8021X_PSK }; 9648a1b9b6aSSam Leffler const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; 9658a1b9b6aSSam Leffler u_int8_t *frm = ie; 9668a1b9b6aSSam Leffler u_int8_t *selcnt; 9678a1b9b6aSSam Leffler 9688a1b9b6aSSam Leffler *frm++ = IEEE80211_ELEMID_RSN; 9698a1b9b6aSSam Leffler *frm++ = 0; /* length filled in below */ 9708a1b9b6aSSam Leffler ADDSHORT(frm, RSN_VERSION); 9718a1b9b6aSSam Leffler 9728a1b9b6aSSam Leffler /* XXX filter out CKIP */ 9738a1b9b6aSSam Leffler 9748a1b9b6aSSam Leffler /* multicast cipher */ 9758a1b9b6aSSam Leffler if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && 9768a1b9b6aSSam Leffler rsn->rsn_mcastkeylen >= 13) 9778a1b9b6aSSam Leffler ADDSELECTOR(frm, wep104_suite); 9788a1b9b6aSSam Leffler else 9798a1b9b6aSSam Leffler ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); 9808a1b9b6aSSam Leffler 9818a1b9b6aSSam Leffler /* unicast cipher list */ 9828a1b9b6aSSam Leffler selcnt = frm; 9838a1b9b6aSSam Leffler ADDSHORT(frm, 0); /* selector count */ 9848a1b9b6aSSam Leffler if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) { 9858a1b9b6aSSam Leffler selcnt[0]++; 9868a1b9b6aSSam Leffler ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]); 9878a1b9b6aSSam Leffler } 9888a1b9b6aSSam Leffler if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) { 9898a1b9b6aSSam Leffler selcnt[0]++; 9908a1b9b6aSSam Leffler ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]); 9918a1b9b6aSSam Leffler } 9928a1b9b6aSSam Leffler 9938a1b9b6aSSam Leffler /* authenticator selector list */ 9948a1b9b6aSSam Leffler selcnt = frm; 9958a1b9b6aSSam Leffler ADDSHORT(frm, 0); /* selector count */ 9968a1b9b6aSSam Leffler if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { 9978a1b9b6aSSam Leffler selcnt[0]++; 9988a1b9b6aSSam Leffler ADDSELECTOR(frm, key_mgt_unspec); 9998a1b9b6aSSam Leffler } 10008a1b9b6aSSam Leffler if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { 10018a1b9b6aSSam Leffler selcnt[0]++; 10028a1b9b6aSSam Leffler ADDSELECTOR(frm, key_mgt_psk); 10038a1b9b6aSSam Leffler } 10048a1b9b6aSSam Leffler 10058a1b9b6aSSam Leffler /* optional capabilities */ 10068a1b9b6aSSam Leffler ADDSHORT(frm, rsn->rsn_caps); 10078a1b9b6aSSam Leffler /* XXX PMKID */ 10088a1b9b6aSSam Leffler 10098a1b9b6aSSam Leffler /* calculate element length */ 10108a1b9b6aSSam Leffler ie[1] = frm - ie - 2; 10118a1b9b6aSSam Leffler KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), 101216574882SSam Leffler ("RSN IE too big, %u > %zu", 10138a1b9b6aSSam Leffler ie[1]+2, sizeof(struct ieee80211_ie_wpa))); 10148a1b9b6aSSam Leffler return frm; 10158a1b9b6aSSam Leffler #undef ADDSELECTOR 10168a1b9b6aSSam Leffler #undef ADDSHORT 10178a1b9b6aSSam Leffler #undef RSN_OUI_BYTES 10188a1b9b6aSSam Leffler } 10198a1b9b6aSSam Leffler 10208a1b9b6aSSam Leffler /* 10218a1b9b6aSSam Leffler * Add a WPA/RSN element to a frame. 10228a1b9b6aSSam Leffler */ 10238a1b9b6aSSam Leffler static u_int8_t * 10248a1b9b6aSSam Leffler ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic) 10258a1b9b6aSSam Leffler { 10268a1b9b6aSSam Leffler 10278a1b9b6aSSam Leffler KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!")); 10288a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA2) 10298a1b9b6aSSam Leffler frm = ieee80211_setup_rsn_ie(ic, frm); 10308a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA1) 10318a1b9b6aSSam Leffler frm = ieee80211_setup_wpa_ie(ic, frm); 10328a1b9b6aSSam Leffler return frm; 10338a1b9b6aSSam Leffler } 10348a1b9b6aSSam Leffler 10358a1b9b6aSSam Leffler #define WME_OUI_BYTES 0x00, 0x50, 0xf2 10368a1b9b6aSSam Leffler /* 10378a1b9b6aSSam Leffler * Add a WME information element to a frame. 10388a1b9b6aSSam Leffler */ 10398a1b9b6aSSam Leffler static u_int8_t * 10408a1b9b6aSSam Leffler ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme) 10418a1b9b6aSSam Leffler { 10428a1b9b6aSSam Leffler static const struct ieee80211_wme_info info = { 10438a1b9b6aSSam Leffler .wme_id = IEEE80211_ELEMID_VENDOR, 10448a1b9b6aSSam Leffler .wme_len = sizeof(struct ieee80211_wme_info) - 2, 10458a1b9b6aSSam Leffler .wme_oui = { WME_OUI_BYTES }, 10468a1b9b6aSSam Leffler .wme_type = WME_OUI_TYPE, 10478a1b9b6aSSam Leffler .wme_subtype = WME_INFO_OUI_SUBTYPE, 10488a1b9b6aSSam Leffler .wme_version = WME_VERSION, 10498a1b9b6aSSam Leffler .wme_info = 0, 10508a1b9b6aSSam Leffler }; 10518a1b9b6aSSam Leffler memcpy(frm, &info, sizeof(info)); 10528a1b9b6aSSam Leffler return frm + sizeof(info); 10538a1b9b6aSSam Leffler } 10548a1b9b6aSSam Leffler 10558a1b9b6aSSam Leffler /* 10568a1b9b6aSSam Leffler * Add a WME parameters element to a frame. 10578a1b9b6aSSam Leffler */ 10588a1b9b6aSSam Leffler static u_int8_t * 10598a1b9b6aSSam Leffler ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme) 10608a1b9b6aSSam Leffler { 10618a1b9b6aSSam Leffler #define SM(_v, _f) (((_v) << _f##_S) & _f) 10628a1b9b6aSSam Leffler #define ADDSHORT(frm, v) do { \ 10638a1b9b6aSSam Leffler frm[0] = (v) & 0xff; \ 10648a1b9b6aSSam Leffler frm[1] = (v) >> 8; \ 10658a1b9b6aSSam Leffler frm += 2; \ 10668a1b9b6aSSam Leffler } while (0) 10678a1b9b6aSSam Leffler /* NB: this works 'cuz a param has an info at the front */ 10688a1b9b6aSSam Leffler static const struct ieee80211_wme_info param = { 10698a1b9b6aSSam Leffler .wme_id = IEEE80211_ELEMID_VENDOR, 10708a1b9b6aSSam Leffler .wme_len = sizeof(struct ieee80211_wme_param) - 2, 10718a1b9b6aSSam Leffler .wme_oui = { WME_OUI_BYTES }, 10728a1b9b6aSSam Leffler .wme_type = WME_OUI_TYPE, 10738a1b9b6aSSam Leffler .wme_subtype = WME_PARAM_OUI_SUBTYPE, 10748a1b9b6aSSam Leffler .wme_version = WME_VERSION, 10758a1b9b6aSSam Leffler }; 10768a1b9b6aSSam Leffler int i; 10778a1b9b6aSSam Leffler 10788a1b9b6aSSam Leffler memcpy(frm, ¶m, sizeof(param)); 10798a1b9b6aSSam Leffler frm += __offsetof(struct ieee80211_wme_info, wme_info); 10808a1b9b6aSSam Leffler *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ 10818a1b9b6aSSam Leffler *frm++ = 0; /* reserved field */ 10828a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 10838a1b9b6aSSam Leffler const struct wmeParams *ac = 10848a1b9b6aSSam Leffler &wme->wme_bssChanParams.cap_wmeParams[i]; 10858a1b9b6aSSam Leffler *frm++ = SM(i, WME_PARAM_ACI) 10868a1b9b6aSSam Leffler | SM(ac->wmep_acm, WME_PARAM_ACM) 10878a1b9b6aSSam Leffler | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) 10888a1b9b6aSSam Leffler ; 10898a1b9b6aSSam Leffler *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) 10908a1b9b6aSSam Leffler | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) 10918a1b9b6aSSam Leffler ; 10928a1b9b6aSSam Leffler ADDSHORT(frm, ac->wmep_txopLimit); 10938a1b9b6aSSam Leffler } 10948a1b9b6aSSam Leffler return frm; 10958a1b9b6aSSam Leffler #undef SM 10968a1b9b6aSSam Leffler #undef ADDSHORT 10978a1b9b6aSSam Leffler } 10988a1b9b6aSSam Leffler #undef WME_OUI_BYTES 10998a1b9b6aSSam Leffler 11000a915fadSSam Leffler /* 1101af8418dcSSam Leffler * Send a probe request frame with the specified ssid 1102af8418dcSSam Leffler * and any optional information element data. 1103af8418dcSSam Leffler */ 1104af8418dcSSam Leffler int 1105af8418dcSSam Leffler ieee80211_send_probereq(struct ieee80211_node *ni, 1106af8418dcSSam Leffler const u_int8_t sa[IEEE80211_ADDR_LEN], 1107af8418dcSSam Leffler const u_int8_t da[IEEE80211_ADDR_LEN], 1108af8418dcSSam Leffler const u_int8_t bssid[IEEE80211_ADDR_LEN], 1109af8418dcSSam Leffler const u_int8_t *ssid, size_t ssidlen, 1110af8418dcSSam Leffler const void *optie, size_t optielen) 1111af8418dcSSam Leffler { 1112af8418dcSSam Leffler struct ieee80211com *ic = ni->ni_ic; 1113af8418dcSSam Leffler enum ieee80211_phymode mode; 1114af8418dcSSam Leffler struct ieee80211_frame *wh; 1115af8418dcSSam Leffler struct mbuf *m; 1116af8418dcSSam Leffler u_int8_t *frm; 1117af8418dcSSam Leffler 1118af8418dcSSam Leffler /* 1119af8418dcSSam Leffler * Hold a reference on the node so it doesn't go away until after 1120af8418dcSSam Leffler * the xmit is complete all the way in the driver. On error we 1121af8418dcSSam Leffler * will remove our reference. 1122af8418dcSSam Leffler */ 1123af8418dcSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, 1124af8418dcSSam Leffler "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 1125af8418dcSSam Leffler __func__, __LINE__, 1126af8418dcSSam Leffler ni, ether_sprintf(ni->ni_macaddr), 1127af8418dcSSam Leffler ieee80211_node_refcnt(ni)+1); 1128af8418dcSSam Leffler ieee80211_ref_node(ni); 1129af8418dcSSam Leffler 1130af8418dcSSam Leffler /* 1131af8418dcSSam Leffler * prreq frame format 1132af8418dcSSam Leffler * [tlv] ssid 1133af8418dcSSam Leffler * [tlv] supported rates 1134af8418dcSSam Leffler * [tlv] extended supported rates 1135af8418dcSSam Leffler * [tlv] user-specified ie's 1136af8418dcSSam Leffler */ 1137af8418dcSSam Leffler m = ieee80211_getmgtframe(&frm, 1138af8418dcSSam Leffler 2 + IEEE80211_NWID_LEN 1139af8418dcSSam Leffler + 2 + IEEE80211_RATE_SIZE 1140af8418dcSSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 1141af8418dcSSam Leffler + (optie != NULL ? optielen : 0) 1142af8418dcSSam Leffler ); 1143af8418dcSSam Leffler if (m == NULL) { 1144af8418dcSSam Leffler ic->ic_stats.is_tx_nobuf++; 1145af8418dcSSam Leffler ieee80211_free_node(ni); 1146af8418dcSSam Leffler return ENOMEM; 1147af8418dcSSam Leffler } 1148af8418dcSSam Leffler 1149af8418dcSSam Leffler frm = ieee80211_add_ssid(frm, ssid, ssidlen); 1150b5c99415SSam Leffler mode = ieee80211_chan2mode(ic, ic->ic_curchan); 1151af8418dcSSam Leffler frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); 1152af8418dcSSam Leffler frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); 1153af8418dcSSam Leffler 1154af8418dcSSam Leffler if (optie != NULL) { 1155af8418dcSSam Leffler memcpy(frm, optie, optielen); 1156af8418dcSSam Leffler frm += optielen; 1157af8418dcSSam Leffler } 1158af8418dcSSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 1159af8418dcSSam Leffler 1160af8418dcSSam Leffler M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 1161af8418dcSSam Leffler if (m == NULL) 1162af8418dcSSam Leffler return ENOMEM; 1163af8418dcSSam Leffler KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null")); 1164af8418dcSSam Leffler m->m_pkthdr.rcvif = (void *)ni; 1165af8418dcSSam Leffler 1166af8418dcSSam Leffler wh = mtod(m, struct ieee80211_frame *); 1167af8418dcSSam Leffler ieee80211_send_setup(ic, ni, wh, 1168af8418dcSSam Leffler IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, 1169af8418dcSSam Leffler sa, da, bssid); 1170af8418dcSSam Leffler /* XXX power management? */ 1171af8418dcSSam Leffler 1172af8418dcSSam Leffler IEEE80211_NODE_STAT(ni, tx_probereq); 1173af8418dcSSam Leffler IEEE80211_NODE_STAT(ni, tx_mgmt); 1174af8418dcSSam Leffler 1175af8418dcSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 1176af8418dcSSam Leffler "[%s] send probe req on channel %u\n", 1177af8418dcSSam Leffler ether_sprintf(wh->i_addr1), 1178b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan)); 1179af8418dcSSam Leffler 1180af8418dcSSam Leffler IF_ENQUEUE(&ic->ic_mgtq, m); 1181af8418dcSSam Leffler if_start(ic->ic_ifp); 1182af8418dcSSam Leffler return 0; 1183af8418dcSSam Leffler } 1184af8418dcSSam Leffler 1185af8418dcSSam Leffler /* 1186667dad55SSam Leffler * Calculate capability information for mgt frames. 1187667dad55SSam Leffler */ 1188667dad55SSam Leffler static u_int16_t 1189667dad55SSam Leffler getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan) 1190667dad55SSam Leffler { 1191667dad55SSam Leffler u_int16_t capinfo; 1192667dad55SSam Leffler 1193667dad55SSam Leffler KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode")); 1194667dad55SSam Leffler 1195667dad55SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 1196667dad55SSam Leffler capinfo = IEEE80211_CAPINFO_ESS; 1197667dad55SSam Leffler else if (ic->ic_opmode == IEEE80211_M_IBSS) 1198667dad55SSam Leffler capinfo = IEEE80211_CAPINFO_IBSS; 1199667dad55SSam Leffler else 1200667dad55SSam Leffler capinfo = 0; 1201667dad55SSam Leffler if (ic->ic_flags & IEEE80211_F_PRIVACY) 1202667dad55SSam Leffler capinfo |= IEEE80211_CAPINFO_PRIVACY; 1203667dad55SSam Leffler if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 1204667dad55SSam Leffler IEEE80211_IS_CHAN_2GHZ(chan)) 1205667dad55SSam Leffler capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 1206667dad55SSam Leffler if (ic->ic_flags & IEEE80211_F_SHSLOT) 1207667dad55SSam Leffler capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 1208667dad55SSam Leffler return capinfo; 1209667dad55SSam Leffler } 1210667dad55SSam Leffler 1211667dad55SSam Leffler /* 12120a915fadSSam Leffler * Send a management frame. The node is for the destination (or ic_bss 12130a915fadSSam Leffler * when in station mode). Nodes other than ic_bss have their reference 12140a915fadSSam Leffler * count bumped to reflect our use for an indeterminant time. 12150a915fadSSam Leffler */ 12161a1e1d21SSam Leffler int 12171a1e1d21SSam Leffler ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, 12181a1e1d21SSam Leffler int type, int arg) 12191a1e1d21SSam Leffler { 12201be50176SSam Leffler #define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) 12211a1e1d21SSam Leffler struct mbuf *m; 12221a1e1d21SSam Leffler u_int8_t *frm; 12231a1e1d21SSam Leffler u_int16_t capinfo; 12248a1b9b6aSSam Leffler int has_challenge, is_shared_key, ret, timer, status; 12251a1e1d21SSam Leffler 12260a915fadSSam Leffler KASSERT(ni != NULL, ("null node")); 12270a915fadSSam Leffler 12280a915fadSSam Leffler /* 12290a915fadSSam Leffler * Hold a reference on the node so it doesn't go away until after 12300a915fadSSam Leffler * the xmit is complete all the way in the driver. On error we 12310a915fadSSam Leffler * will remove our reference. 12320a915fadSSam Leffler */ 12338a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, 123449a15236SSam Leffler "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 12358a1b9b6aSSam Leffler __func__, __LINE__, 123649a15236SSam Leffler ni, ether_sprintf(ni->ni_macaddr), 12378a1b9b6aSSam Leffler ieee80211_node_refcnt(ni)+1); 12380a915fadSSam Leffler ieee80211_ref_node(ni); 12398a1b9b6aSSam Leffler 12401a1e1d21SSam Leffler timer = 0; 12411a1e1d21SSam Leffler switch (type) { 12421a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 12431a1e1d21SSam Leffler /* 12441a1e1d21SSam Leffler * probe response frame format 12451a1e1d21SSam Leffler * [8] time stamp 12461a1e1d21SSam Leffler * [2] beacon interval 12471a1e1d21SSam Leffler * [2] cabability information 12481a1e1d21SSam Leffler * [tlv] ssid 12491a1e1d21SSam Leffler * [tlv] supported rates 1250b83d2112SSam Leffler * [tlv] parameter set (FH/DS) 12511a1e1d21SSam Leffler * [tlv] parameter set (IBSS) 12528a1b9b6aSSam Leffler * [tlv] extended rate phy (ERP) 12531a1e1d21SSam Leffler * [tlv] extended supported rates 12548a1b9b6aSSam Leffler * [tlv] WPA 1255d6ec172cSSam Leffler * [tlv] WME (optional) 12561a1e1d21SSam Leffler */ 12578a1b9b6aSSam Leffler m = ieee80211_getmgtframe(&frm, 12588a1b9b6aSSam Leffler 8 12598a1b9b6aSSam Leffler + sizeof(u_int16_t) 12608a1b9b6aSSam Leffler + sizeof(u_int16_t) 12618a1b9b6aSSam Leffler + 2 + IEEE80211_NWID_LEN 12621a1e1d21SSam Leffler + 2 + IEEE80211_RATE_SIZE 12638a1b9b6aSSam Leffler + 7 /* max(7,3) */ 12641a1e1d21SSam Leffler + 6 12658a1b9b6aSSam Leffler + 3 12668a1b9b6aSSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 12678a1b9b6aSSam Leffler /* XXX !WPA1+WPA2 fits w/o a cluster */ 12688a1b9b6aSSam Leffler + (ic->ic_flags & IEEE80211_F_WPA ? 12698a1b9b6aSSam Leffler 2*sizeof(struct ieee80211_ie_wpa) : 0) 1270d6ec172cSSam Leffler + sizeof(struct ieee80211_wme_param) 12718a1b9b6aSSam Leffler ); 12721a1e1d21SSam Leffler if (m == NULL) 12738a1b9b6aSSam Leffler senderr(ENOMEM, is_tx_nobuf); 12741a1e1d21SSam Leffler 12751a1e1d21SSam Leffler memset(frm, 0, 8); /* timestamp should be filled later */ 12761a1e1d21SSam Leffler frm += 8; 12771a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); 12781a1e1d21SSam Leffler frm += 2; 1279667dad55SSam Leffler capinfo = getcapinfo(ic, ic->ic_curchan); 12801a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(capinfo); 12811a1e1d21SSam Leffler frm += 2; 12821a1e1d21SSam Leffler 12831a1e1d21SSam Leffler frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, 12841a1e1d21SSam Leffler ic->ic_bss->ni_esslen); 12858a1b9b6aSSam Leffler frm = ieee80211_add_rates(frm, &ni->ni_rates); 12861a1e1d21SSam Leffler 1287b83d2112SSam Leffler if (ic->ic_phytype == IEEE80211_T_FH) { 1288b83d2112SSam Leffler *frm++ = IEEE80211_ELEMID_FHPARMS; 1289b83d2112SSam Leffler *frm++ = 5; 1290b83d2112SSam Leffler *frm++ = ni->ni_fhdwell & 0x00ff; 1291b83d2112SSam Leffler *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff; 1292b83d2112SSam Leffler *frm++ = IEEE80211_FH_CHANSET( 1293b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan)); 1294b83d2112SSam Leffler *frm++ = IEEE80211_FH_CHANPAT( 1295b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan)); 1296b83d2112SSam Leffler *frm++ = ni->ni_fhindex; 1297b83d2112SSam Leffler } else { 1298b83d2112SSam Leffler *frm++ = IEEE80211_ELEMID_DSPARMS; 1299b83d2112SSam Leffler *frm++ = 1; 1300b5c99415SSam Leffler *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan); 1301b83d2112SSam Leffler } 1302b83d2112SSam Leffler 13031a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 13041a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_IBSSPARMS; 13051a1e1d21SSam Leffler *frm++ = 2; 13061a1e1d21SSam Leffler *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 13071a1e1d21SSam Leffler } 13088a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA) 13098a1b9b6aSSam Leffler frm = ieee80211_add_wpa(frm, ic); 13108a1b9b6aSSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11G) 13118a1b9b6aSSam Leffler frm = ieee80211_add_erp(frm, ic); 13128a1b9b6aSSam Leffler frm = ieee80211_add_xrates(frm, &ni->ni_rates); 1313d6ec172cSSam Leffler if (ic->ic_flags & IEEE80211_F_WME) 1314d6ec172cSSam Leffler frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 13151a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 13161a1e1d21SSam Leffler break; 13171a1e1d21SSam Leffler 13181a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 13198a1b9b6aSSam Leffler status = arg >> 16; 13208a1b9b6aSSam Leffler arg &= 0xffff; 13218a1b9b6aSSam Leffler has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || 13228a1b9b6aSSam Leffler arg == IEEE80211_AUTH_SHARED_RESPONSE) && 13238a1b9b6aSSam Leffler ni->ni_challenge != NULL); 13248a1b9b6aSSam Leffler 13258a1b9b6aSSam Leffler /* 13268a1b9b6aSSam Leffler * Deduce whether we're doing open authentication or 13278a1b9b6aSSam Leffler * shared key authentication. We do the latter if 13288a1b9b6aSSam Leffler * we're in the middle of a shared key authentication 13298a1b9b6aSSam Leffler * handshake or if we're initiating an authentication 13308a1b9b6aSSam Leffler * request and configured to use shared key. 13318a1b9b6aSSam Leffler */ 13328a1b9b6aSSam Leffler is_shared_key = has_challenge || 13338a1b9b6aSSam Leffler arg >= IEEE80211_AUTH_SHARED_RESPONSE || 13348a1b9b6aSSam Leffler (arg == IEEE80211_AUTH_SHARED_REQUEST && 13358a1b9b6aSSam Leffler ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); 13368a1b9b6aSSam Leffler 13378a1b9b6aSSam Leffler m = ieee80211_getmgtframe(&frm, 13388a1b9b6aSSam Leffler 3 * sizeof(u_int16_t) 13398a1b9b6aSSam Leffler + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? 13408a1b9b6aSSam Leffler sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0) 13418a1b9b6aSSam Leffler ); 13421a1e1d21SSam Leffler if (m == NULL) 13438a1b9b6aSSam Leffler senderr(ENOMEM, is_tx_nobuf); 13448a1b9b6aSSam Leffler 13458a1b9b6aSSam Leffler ((u_int16_t *)frm)[0] = 13468a1b9b6aSSam Leffler (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) 13478a1b9b6aSSam Leffler : htole16(IEEE80211_AUTH_ALG_OPEN); 13481a1e1d21SSam Leffler ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ 13498a1b9b6aSSam Leffler ((u_int16_t *)frm)[2] = htole16(status);/* status */ 13508a1b9b6aSSam Leffler 13518a1b9b6aSSam Leffler if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { 13528a1b9b6aSSam Leffler ((u_int16_t *)frm)[3] = 13538a1b9b6aSSam Leffler htole16((IEEE80211_CHALLENGE_LEN << 8) | 13548a1b9b6aSSam Leffler IEEE80211_ELEMID_CHALLENGE); 13558a1b9b6aSSam Leffler memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge, 13568a1b9b6aSSam Leffler IEEE80211_CHALLENGE_LEN); 13578a1b9b6aSSam Leffler m->m_pkthdr.len = m->m_len = 13588a1b9b6aSSam Leffler 4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN; 13598a1b9b6aSSam Leffler if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { 13608a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 13618a1b9b6aSSam Leffler "[%s] request encrypt frame (%s)\n", 13628a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), __func__); 13638a1b9b6aSSam Leffler m->m_flags |= M_LINK0; /* WEP-encrypt, please */ 13648a1b9b6aSSam Leffler } 13658a1b9b6aSSam Leffler } else 13668a1b9b6aSSam Leffler m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t); 13678a1b9b6aSSam Leffler 13688a1b9b6aSSam Leffler /* XXX not right for shared key */ 13698a1b9b6aSSam Leffler if (status == IEEE80211_STATUS_SUCCESS) 13708a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_auth); 13718a1b9b6aSSam Leffler else 13728a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_auth_fail); 13738a1b9b6aSSam Leffler 13741a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 13751a1e1d21SSam Leffler timer = IEEE80211_TRANS_WAIT; 13761a1e1d21SSam Leffler break; 13771a1e1d21SSam Leffler 13781a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 13798a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, 13808a1b9b6aSSam Leffler "[%s] send station deauthenticate (reason %d)\n", 13811a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), arg); 13828a1b9b6aSSam Leffler m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); 13831a1e1d21SSam Leffler if (m == NULL) 13848a1b9b6aSSam Leffler senderr(ENOMEM, is_tx_nobuf); 13858a1b9b6aSSam Leffler *(u_int16_t *)frm = htole16(arg); /* reason */ 13868a1b9b6aSSam Leffler m->m_pkthdr.len = m->m_len = sizeof(u_int16_t); 13878a1b9b6aSSam Leffler 13888a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_deauth); 13898a1b9b6aSSam Leffler IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); 13908a1b9b6aSSam Leffler 1391e4918ecdSSam Leffler ieee80211_node_unauthorize(ni); /* port closed */ 13921a1e1d21SSam Leffler break; 13931a1e1d21SSam Leffler 13941a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 13951a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 13961a1e1d21SSam Leffler /* 13971a1e1d21SSam Leffler * asreq frame format 13981a1e1d21SSam Leffler * [2] capability information 13991a1e1d21SSam Leffler * [2] listen interval 14001a1e1d21SSam Leffler * [6*] current AP address (reassoc only) 14011a1e1d21SSam Leffler * [tlv] ssid 14021a1e1d21SSam Leffler * [tlv] supported rates 14031a1e1d21SSam Leffler * [tlv] extended supported rates 14048a1b9b6aSSam Leffler * [tlv] WME 14058a1b9b6aSSam Leffler * [tlv] user-specified ie's 14061a1e1d21SSam Leffler */ 14078a1b9b6aSSam Leffler m = ieee80211_getmgtframe(&frm, 14088a1b9b6aSSam Leffler sizeof(u_int16_t) 14091a1e1d21SSam Leffler + sizeof(u_int16_t) 14101a1e1d21SSam Leffler + IEEE80211_ADDR_LEN 14118a1b9b6aSSam Leffler + 2 + IEEE80211_NWID_LEN 14121a1e1d21SSam Leffler + 2 + IEEE80211_RATE_SIZE 14138a1b9b6aSSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 14148a1b9b6aSSam Leffler + sizeof(struct ieee80211_wme_info) 14158a1b9b6aSSam Leffler + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0) 14168a1b9b6aSSam Leffler ); 14171a1e1d21SSam Leffler if (m == NULL) 14188a1b9b6aSSam Leffler senderr(ENOMEM, is_tx_nobuf); 14191a1e1d21SSam Leffler 1420667dad55SSam Leffler KASSERT(ic->ic_opmode == IEEE80211_M_STA, 1421667dad55SSam Leffler ("wrong mode %u", ic->ic_opmode)); 1422667dad55SSam Leffler capinfo = IEEE80211_CAPINFO_ESS; 14238a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_PRIVACY) 14241a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_PRIVACY; 14254f2e09c4SSam Leffler /* 14264f2e09c4SSam Leffler * NB: Some 11a AP's reject the request when 14274f2e09c4SSam Leffler * short premable is set. 14284f2e09c4SSam Leffler */ 14294f2e09c4SSam Leffler if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 1430b5c99415SSam Leffler IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 14311a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 14328a1b9b6aSSam Leffler if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) && 14338a1b9b6aSSam Leffler (ic->ic_caps & IEEE80211_C_SHSLOT)) 14341a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 14351a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(capinfo); 14361a1e1d21SSam Leffler frm += 2; 14371a1e1d21SSam Leffler 14381a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(ic->ic_lintval); 14391a1e1d21SSam Leffler frm += 2; 14401a1e1d21SSam Leffler 14411a1e1d21SSam Leffler if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 14421a1e1d21SSam Leffler IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); 14431a1e1d21SSam Leffler frm += IEEE80211_ADDR_LEN; 14441a1e1d21SSam Leffler } 14451a1e1d21SSam Leffler 14461a1e1d21SSam Leffler frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 14471a1e1d21SSam Leffler frm = ieee80211_add_rates(frm, &ni->ni_rates); 14481a1e1d21SSam Leffler frm = ieee80211_add_xrates(frm, &ni->ni_rates); 14498a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) 14508a1b9b6aSSam Leffler frm = ieee80211_add_wme_info(frm, &ic->ic_wme); 14518a1b9b6aSSam Leffler if (ic->ic_opt_ie != NULL) { 14528a1b9b6aSSam Leffler memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); 14538a1b9b6aSSam Leffler frm += ic->ic_opt_ie_len; 14548a1b9b6aSSam Leffler } 14551a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 14561a1e1d21SSam Leffler 14571a1e1d21SSam Leffler timer = IEEE80211_TRANS_WAIT; 14581a1e1d21SSam Leffler break; 14591a1e1d21SSam Leffler 14601a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 14611a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 14621a1e1d21SSam Leffler /* 14631a1e1d21SSam Leffler * asreq frame format 14641a1e1d21SSam Leffler * [2] capability information 14651a1e1d21SSam Leffler * [2] status 14661a1e1d21SSam Leffler * [2] association ID 14671a1e1d21SSam Leffler * [tlv] supported rates 14681a1e1d21SSam Leffler * [tlv] extended supported rates 14698a1b9b6aSSam Leffler * [tlv] WME (if enabled and STA enabled) 14701a1e1d21SSam Leffler */ 14718a1b9b6aSSam Leffler m = ieee80211_getmgtframe(&frm, 14728a1b9b6aSSam Leffler sizeof(u_int16_t) 14731a1e1d21SSam Leffler + sizeof(u_int16_t) 14741a1e1d21SSam Leffler + sizeof(u_int16_t) 14751a1e1d21SSam Leffler + 2 + IEEE80211_RATE_SIZE 14768a1b9b6aSSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 14778a1b9b6aSSam Leffler + sizeof(struct ieee80211_wme_param) 14788a1b9b6aSSam Leffler ); 14791a1e1d21SSam Leffler if (m == NULL) 14808a1b9b6aSSam Leffler senderr(ENOMEM, is_tx_nobuf); 14811a1e1d21SSam Leffler 1482667dad55SSam Leffler capinfo = getcapinfo(ic, ic->ic_curchan); 14831a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(capinfo); 14841a1e1d21SSam Leffler frm += 2; 14851a1e1d21SSam Leffler 14861a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(arg); /* status */ 14871a1e1d21SSam Leffler frm += 2; 14881a1e1d21SSam Leffler 14898a1b9b6aSSam Leffler if (arg == IEEE80211_STATUS_SUCCESS) { 14901a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(ni->ni_associd); 14918a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_assoc); 14928a1b9b6aSSam Leffler } else 14938a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_assoc_fail); 14941a1e1d21SSam Leffler frm += 2; 14951a1e1d21SSam Leffler 14961a1e1d21SSam Leffler frm = ieee80211_add_rates(frm, &ni->ni_rates); 14971a1e1d21SSam Leffler frm = ieee80211_add_xrates(frm, &ni->ni_rates); 14988a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) 14998a1b9b6aSSam Leffler frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 15001a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 15011a1e1d21SSam Leffler break; 15021a1e1d21SSam Leffler 15031a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DISASSOC: 15048a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, 15058a1b9b6aSSam Leffler "[%s] send station disassociate (reason %d)\n", 15061a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), arg); 15078a1b9b6aSSam Leffler m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); 15081a1e1d21SSam Leffler if (m == NULL) 15098a1b9b6aSSam Leffler senderr(ENOMEM, is_tx_nobuf); 15108a1b9b6aSSam Leffler *(u_int16_t *)frm = htole16(arg); /* reason */ 15118a1b9b6aSSam Leffler m->m_pkthdr.len = m->m_len = sizeof(u_int16_t); 15128a1b9b6aSSam Leffler 15138a1b9b6aSSam Leffler IEEE80211_NODE_STAT(ni, tx_disassoc); 15148a1b9b6aSSam Leffler IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); 15151a1e1d21SSam Leffler break; 15161a1e1d21SSam Leffler 15171a1e1d21SSam Leffler default: 15188a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 15198a1b9b6aSSam Leffler "[%s] invalid mgmt frame type %u\n", 15208a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), type); 15211be50176SSam Leffler senderr(EINVAL, is_tx_unknownmgt); 15220a915fadSSam Leffler /* NOTREACHED */ 15231a1e1d21SSam Leffler } 15246d049ab3SSam Leffler ret = ieee80211_mgmt_output(ic, ni, m, type, timer); 15256d049ab3SSam Leffler if (ret != 0) { 15260a915fadSSam Leffler bad: 15278a1b9b6aSSam Leffler ieee80211_free_node(ni); 15280a915fadSSam Leffler } 15291a1e1d21SSam Leffler return ret; 15300a915fadSSam Leffler #undef senderr 15311a1e1d21SSam Leffler } 15328a1b9b6aSSam Leffler 15338a1b9b6aSSam Leffler /* 15348a1b9b6aSSam Leffler * Allocate a beacon frame and fillin the appropriate bits. 15358a1b9b6aSSam Leffler */ 15368a1b9b6aSSam Leffler struct mbuf * 15378a1b9b6aSSam Leffler ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, 15388a1b9b6aSSam Leffler struct ieee80211_beacon_offsets *bo) 15398a1b9b6aSSam Leffler { 15408a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 15418a1b9b6aSSam Leffler struct ieee80211_frame *wh; 15428a1b9b6aSSam Leffler struct mbuf *m; 15438a1b9b6aSSam Leffler int pktlen; 15448a1b9b6aSSam Leffler u_int8_t *frm, *efrm; 15458a1b9b6aSSam Leffler u_int16_t capinfo; 15468a1b9b6aSSam Leffler struct ieee80211_rateset *rs; 15478a1b9b6aSSam Leffler 15488a1b9b6aSSam Leffler /* 15498a1b9b6aSSam Leffler * beacon frame format 15508a1b9b6aSSam Leffler * [8] time stamp 15518a1b9b6aSSam Leffler * [2] beacon interval 15528a1b9b6aSSam Leffler * [2] cabability information 15538a1b9b6aSSam Leffler * [tlv] ssid 15548a1b9b6aSSam Leffler * [tlv] supported rates 15558a1b9b6aSSam Leffler * [3] parameter set (DS) 15568a1b9b6aSSam Leffler * [tlv] parameter set (IBSS/TIM) 15578a1b9b6aSSam Leffler * [tlv] extended rate phy (ERP) 15588a1b9b6aSSam Leffler * [tlv] extended supported rates 15598a1b9b6aSSam Leffler * [tlv] WME parameters 15608a1b9b6aSSam Leffler * [tlv] WPA/RSN parameters 15618a1b9b6aSSam Leffler * XXX Vendor-specific OIDs (e.g. Atheros) 15628a1b9b6aSSam Leffler * NB: we allocate the max space required for the TIM bitmap. 15638a1b9b6aSSam Leffler */ 15648a1b9b6aSSam Leffler rs = &ni->ni_rates; 15658a1b9b6aSSam Leffler pktlen = 8 /* time stamp */ 15668a1b9b6aSSam Leffler + sizeof(u_int16_t) /* beacon interval */ 15678a1b9b6aSSam Leffler + sizeof(u_int16_t) /* capabilities */ 15688a1b9b6aSSam Leffler + 2 + ni->ni_esslen /* ssid */ 15698a1b9b6aSSam Leffler + 2 + IEEE80211_RATE_SIZE /* supported rates */ 15708a1b9b6aSSam Leffler + 2 + 1 /* DS parameters */ 15718a1b9b6aSSam Leffler + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */ 15728a1b9b6aSSam Leffler + 2 + 1 /* ERP */ 15738a1b9b6aSSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 15748a1b9b6aSSam Leffler + (ic->ic_caps & IEEE80211_C_WME ? /* WME */ 15758a1b9b6aSSam Leffler sizeof(struct ieee80211_wme_param) : 0) 15768a1b9b6aSSam Leffler + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ 15778a1b9b6aSSam Leffler 2*sizeof(struct ieee80211_ie_wpa) : 0) 15788a1b9b6aSSam Leffler ; 15798a1b9b6aSSam Leffler m = ieee80211_getmgtframe(&frm, pktlen); 15808a1b9b6aSSam Leffler if (m == NULL) { 15818a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 15828a1b9b6aSSam Leffler "%s: cannot get buf; size %u\n", __func__, pktlen); 15838a1b9b6aSSam Leffler ic->ic_stats.is_tx_nobuf++; 15848a1b9b6aSSam Leffler return NULL; 15858a1b9b6aSSam Leffler } 15868a1b9b6aSSam Leffler 15878a1b9b6aSSam Leffler memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ 15888a1b9b6aSSam Leffler frm += 8; 15898a1b9b6aSSam Leffler *(u_int16_t *)frm = htole16(ni->ni_intval); 15908a1b9b6aSSam Leffler frm += 2; 1591667dad55SSam Leffler capinfo = getcapinfo(ic, ni->ni_chan); 15928a1b9b6aSSam Leffler bo->bo_caps = (u_int16_t *)frm; 15938a1b9b6aSSam Leffler *(u_int16_t *)frm = htole16(capinfo); 15948a1b9b6aSSam Leffler frm += 2; 15958a1b9b6aSSam Leffler *frm++ = IEEE80211_ELEMID_SSID; 15968a1b9b6aSSam Leffler if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) { 15978a1b9b6aSSam Leffler *frm++ = ni->ni_esslen; 15988a1b9b6aSSam Leffler memcpy(frm, ni->ni_essid, ni->ni_esslen); 15998a1b9b6aSSam Leffler frm += ni->ni_esslen; 16008a1b9b6aSSam Leffler } else 16018a1b9b6aSSam Leffler *frm++ = 0; 16028a1b9b6aSSam Leffler frm = ieee80211_add_rates(frm, rs); 16038a1b9b6aSSam Leffler if (ic->ic_curmode != IEEE80211_MODE_FH) { 16048a1b9b6aSSam Leffler *frm++ = IEEE80211_ELEMID_DSPARMS; 16058a1b9b6aSSam Leffler *frm++ = 1; 16068a1b9b6aSSam Leffler *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); 16078a1b9b6aSSam Leffler } 16088a1b9b6aSSam Leffler bo->bo_tim = frm; 16098a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 16108a1b9b6aSSam Leffler *frm++ = IEEE80211_ELEMID_IBSSPARMS; 16118a1b9b6aSSam Leffler *frm++ = 2; 16128a1b9b6aSSam Leffler *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 16138a1b9b6aSSam Leffler bo->bo_tim_len = 0; 1614667dad55SSam Leffler } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 16158a1b9b6aSSam Leffler struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; 16168a1b9b6aSSam Leffler 16178a1b9b6aSSam Leffler tie->tim_ie = IEEE80211_ELEMID_TIM; 16188a1b9b6aSSam Leffler tie->tim_len = 4; /* length */ 16198a1b9b6aSSam Leffler tie->tim_count = 0; /* DTIM count */ 16208a1b9b6aSSam Leffler tie->tim_period = ic->ic_dtim_period; /* DTIM period */ 16218a1b9b6aSSam Leffler tie->tim_bitctl = 0; /* bitmap control */ 16228a1b9b6aSSam Leffler tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ 16238a1b9b6aSSam Leffler frm += sizeof(struct ieee80211_tim_ie); 16248a1b9b6aSSam Leffler bo->bo_tim_len = 1; 16258a1b9b6aSSam Leffler } 16268a1b9b6aSSam Leffler bo->bo_trailer = frm; 16278a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WME) { 16288a1b9b6aSSam Leffler bo->bo_wme = frm; 16298a1b9b6aSSam Leffler frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 16305aa17efaSSam Leffler ic->ic_flags &= ~IEEE80211_F_WMEUPDATE; 16318a1b9b6aSSam Leffler } 16328a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA) 16338a1b9b6aSSam Leffler frm = ieee80211_add_wpa(frm, ic); 16340912bac9SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11G) { 16350912bac9SSam Leffler bo->bo_erp = frm; 16368a1b9b6aSSam Leffler frm = ieee80211_add_erp(frm, ic); 16370912bac9SSam Leffler } 16388a1b9b6aSSam Leffler efrm = ieee80211_add_xrates(frm, rs); 16398a1b9b6aSSam Leffler bo->bo_trailer_len = efrm - bo->bo_trailer; 16408a1b9b6aSSam Leffler m->m_pkthdr.len = m->m_len = efrm - mtod(m, u_int8_t *); 16418a1b9b6aSSam Leffler 16428a1b9b6aSSam Leffler M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 16438a1b9b6aSSam Leffler KASSERT(m != NULL, ("no space for 802.11 header?")); 16448a1b9b6aSSam Leffler wh = mtod(m, struct ieee80211_frame *); 16458a1b9b6aSSam Leffler wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 16468a1b9b6aSSam Leffler IEEE80211_FC0_SUBTYPE_BEACON; 16478a1b9b6aSSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 16488a1b9b6aSSam Leffler *(u_int16_t *)wh->i_dur = 0; 16498a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 16508a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 16518a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 16528a1b9b6aSSam Leffler *(u_int16_t *)wh->i_seq = 0; 16538a1b9b6aSSam Leffler 16548a1b9b6aSSam Leffler return m; 16558a1b9b6aSSam Leffler } 16568a1b9b6aSSam Leffler 16578a1b9b6aSSam Leffler /* 16588a1b9b6aSSam Leffler * Update the dynamic parts of a beacon frame based on the current state. 16598a1b9b6aSSam Leffler */ 16608a1b9b6aSSam Leffler int 16618a1b9b6aSSam Leffler ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, 16628a1b9b6aSSam Leffler struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) 16638a1b9b6aSSam Leffler { 16648a1b9b6aSSam Leffler int len_changed = 0; 16658a1b9b6aSSam Leffler u_int16_t capinfo; 16668a1b9b6aSSam Leffler 16678a1b9b6aSSam Leffler IEEE80211_BEACON_LOCK(ic); 16688a1b9b6aSSam Leffler /* XXX faster to recalculate entirely or just changes? */ 1669667dad55SSam Leffler capinfo = getcapinfo(ic, ni->ni_chan); 16708a1b9b6aSSam Leffler *bo->bo_caps = htole16(capinfo); 16718a1b9b6aSSam Leffler 16728a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WME) { 16738a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 16748a1b9b6aSSam Leffler 16758a1b9b6aSSam Leffler /* 16768a1b9b6aSSam Leffler * Check for agressive mode change. When there is 16778a1b9b6aSSam Leffler * significant high priority traffic in the BSS 16788a1b9b6aSSam Leffler * throttle back BE traffic by using conservative 16798a1b9b6aSSam Leffler * parameters. Otherwise BE uses agressive params 16808a1b9b6aSSam Leffler * to optimize performance of legacy/non-QoS traffic. 16818a1b9b6aSSam Leffler */ 16828a1b9b6aSSam Leffler if (wme->wme_flags & WME_F_AGGRMODE) { 16838a1b9b6aSSam Leffler if (wme->wme_hipri_traffic > 16848a1b9b6aSSam Leffler wme->wme_hipri_switch_thresh) { 16858a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 16868a1b9b6aSSam Leffler "%s: traffic %u, disable aggressive mode\n", 16878a1b9b6aSSam Leffler __func__, wme->wme_hipri_traffic); 16888a1b9b6aSSam Leffler wme->wme_flags &= ~WME_F_AGGRMODE; 16898a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(ic); 16908a1b9b6aSSam Leffler wme->wme_hipri_traffic = 16918a1b9b6aSSam Leffler wme->wme_hipri_switch_hysteresis; 16928a1b9b6aSSam Leffler } else 16938a1b9b6aSSam Leffler wme->wme_hipri_traffic = 0; 16948a1b9b6aSSam Leffler } else { 16958a1b9b6aSSam Leffler if (wme->wme_hipri_traffic <= 16968a1b9b6aSSam Leffler wme->wme_hipri_switch_thresh) { 16978a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 16988a1b9b6aSSam Leffler "%s: traffic %u, enable aggressive mode\n", 16998a1b9b6aSSam Leffler __func__, wme->wme_hipri_traffic); 17008a1b9b6aSSam Leffler wme->wme_flags |= WME_F_AGGRMODE; 17018a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(ic); 17028a1b9b6aSSam Leffler wme->wme_hipri_traffic = 0; 17038a1b9b6aSSam Leffler } else 17048a1b9b6aSSam Leffler wme->wme_hipri_traffic = 17058a1b9b6aSSam Leffler wme->wme_hipri_switch_hysteresis; 17068a1b9b6aSSam Leffler } 17078a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WMEUPDATE) { 17088a1b9b6aSSam Leffler (void) ieee80211_add_wme_param(bo->bo_wme, wme); 17098a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_WMEUPDATE; 17108a1b9b6aSSam Leffler } 17118a1b9b6aSSam Leffler } 17128a1b9b6aSSam Leffler 17138a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/ 17148a1b9b6aSSam Leffler struct ieee80211_tim_ie *tie = 17158a1b9b6aSSam Leffler (struct ieee80211_tim_ie *) bo->bo_tim; 17168a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_TIMUPDATE) { 17178a1b9b6aSSam Leffler u_int timlen, timoff, i; 17188a1b9b6aSSam Leffler /* 17198a1b9b6aSSam Leffler * ATIM/DTIM needs updating. If it fits in the 17208a1b9b6aSSam Leffler * current space allocated then just copy in the 17218a1b9b6aSSam Leffler * new bits. Otherwise we need to move any trailing 17228a1b9b6aSSam Leffler * data to make room. Note that we know there is 17238a1b9b6aSSam Leffler * contiguous space because ieee80211_beacon_allocate 17248a1b9b6aSSam Leffler * insures there is space in the mbuf to write a 17258a1b9b6aSSam Leffler * maximal-size virtual bitmap (based on ic_max_aid). 17268a1b9b6aSSam Leffler */ 17278a1b9b6aSSam Leffler /* 17288a1b9b6aSSam Leffler * Calculate the bitmap size and offset, copy any 17298a1b9b6aSSam Leffler * trailer out of the way, and then copy in the 17308a1b9b6aSSam Leffler * new bitmap and update the information element. 17318a1b9b6aSSam Leffler * Note that the tim bitmap must contain at least 17328a1b9b6aSSam Leffler * one byte and any offset must be even. 17338a1b9b6aSSam Leffler */ 17348a1b9b6aSSam Leffler if (ic->ic_ps_pending != 0) { 17358a1b9b6aSSam Leffler timoff = 128; /* impossibly large */ 17368a1b9b6aSSam Leffler for (i = 0; i < ic->ic_tim_len; i++) 17378a1b9b6aSSam Leffler if (ic->ic_tim_bitmap[i]) { 17388a1b9b6aSSam Leffler timoff = i &~ 1; 17398a1b9b6aSSam Leffler break; 17408a1b9b6aSSam Leffler } 17418a1b9b6aSSam Leffler KASSERT(timoff != 128, ("tim bitmap empty!")); 17428a1b9b6aSSam Leffler for (i = ic->ic_tim_len-1; i >= timoff; i--) 17438a1b9b6aSSam Leffler if (ic->ic_tim_bitmap[i]) 17448a1b9b6aSSam Leffler break; 17458a1b9b6aSSam Leffler timlen = 1 + (i - timoff); 17468a1b9b6aSSam Leffler } else { 17478a1b9b6aSSam Leffler timoff = 0; 17488a1b9b6aSSam Leffler timlen = 1; 17498a1b9b6aSSam Leffler } 17508a1b9b6aSSam Leffler if (timlen != bo->bo_tim_len) { 17518a1b9b6aSSam Leffler /* copy up/down trailer */ 17520912bac9SSam Leffler int adjust = tie->tim_bitmap+timlen 17530912bac9SSam Leffler - bo->bo_trailer; 17540912bac9SSam Leffler ovbcopy(bo->bo_trailer, bo->bo_trailer+adjust, 17558a1b9b6aSSam Leffler bo->bo_trailer_len); 17560912bac9SSam Leffler bo->bo_trailer += adjust; 17570912bac9SSam Leffler bo->bo_wme += adjust; 17580912bac9SSam Leffler bo->bo_erp += adjust; 17598a1b9b6aSSam Leffler bo->bo_tim_len = timlen; 17608a1b9b6aSSam Leffler 17618a1b9b6aSSam Leffler /* update information element */ 17628a1b9b6aSSam Leffler tie->tim_len = 3 + timlen; 17638a1b9b6aSSam Leffler tie->tim_bitctl = timoff; 17648a1b9b6aSSam Leffler len_changed = 1; 17658a1b9b6aSSam Leffler } 17668a1b9b6aSSam Leffler memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff, 17678a1b9b6aSSam Leffler bo->bo_tim_len); 17688a1b9b6aSSam Leffler 17698a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_TIMUPDATE; 17708a1b9b6aSSam Leffler 17718a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 17728a1b9b6aSSam Leffler "%s: TIM updated, pending %u, off %u, len %u\n", 17738a1b9b6aSSam Leffler __func__, ic->ic_ps_pending, timoff, timlen); 17748a1b9b6aSSam Leffler } 17758a1b9b6aSSam Leffler /* count down DTIM period */ 17768a1b9b6aSSam Leffler if (tie->tim_count == 0) 17778a1b9b6aSSam Leffler tie->tim_count = tie->tim_period - 1; 17788a1b9b6aSSam Leffler else 17798a1b9b6aSSam Leffler tie->tim_count--; 17808a1b9b6aSSam Leffler /* update state for buffered multicast frames on DTIM */ 1781a196b35fSSam Leffler if (mcast && tie->tim_count == 0) 17828a1b9b6aSSam Leffler tie->tim_bitctl |= 1; 17838a1b9b6aSSam Leffler else 17848a1b9b6aSSam Leffler tie->tim_bitctl &= ~1; 17850912bac9SSam Leffler if (ic->ic_flags_ext & IEEE80211_FEXT_ERPUPDATE) { 17860912bac9SSam Leffler /* 17870912bac9SSam Leffler * ERP element needs updating. 17880912bac9SSam Leffler */ 17890912bac9SSam Leffler (void) ieee80211_add_erp(bo->bo_erp, ic); 17900912bac9SSam Leffler ic->ic_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE; 17910912bac9SSam Leffler } 17928a1b9b6aSSam Leffler } 17938a1b9b6aSSam Leffler IEEE80211_BEACON_UNLOCK(ic); 17948a1b9b6aSSam Leffler 17958a1b9b6aSSam Leffler return len_changed; 17968a1b9b6aSSam Leffler } 17978a1b9b6aSSam Leffler 17988a1b9b6aSSam Leffler /* 17998a1b9b6aSSam Leffler * Save an outbound packet for a node in power-save sleep state. 18008a1b9b6aSSam Leffler * The new packet is placed on the node's saved queue, and the TIM 18018a1b9b6aSSam Leffler * is changed, if necessary. 18028a1b9b6aSSam Leffler */ 18038a1b9b6aSSam Leffler void 18048a1b9b6aSSam Leffler ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, 18058a1b9b6aSSam Leffler struct mbuf *m) 18068a1b9b6aSSam Leffler { 18078a1b9b6aSSam Leffler int qlen, age; 18088a1b9b6aSSam Leffler 18098a1b9b6aSSam Leffler IEEE80211_NODE_SAVEQ_LOCK(ni); 18108a1b9b6aSSam Leffler if (_IF_QFULL(&ni->ni_savedq)) { 18118a1b9b6aSSam Leffler _IF_DROP(&ni->ni_savedq); 18128a1b9b6aSSam Leffler IEEE80211_NODE_SAVEQ_UNLOCK(ni); 18138a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 18148a1b9b6aSSam Leffler "[%s] pwr save q overflow, drops %d (size %d)\n", 18158a1b9b6aSSam Leffler ether_sprintf(ni->ni_macaddr), 18168a1b9b6aSSam Leffler ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE); 18178a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 18188a1b9b6aSSam Leffler if (ieee80211_msg_dumppkts(ic)) 18198a1b9b6aSSam Leffler ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1); 18208a1b9b6aSSam Leffler #endif 18218a1b9b6aSSam Leffler m_freem(m); 18228a1b9b6aSSam Leffler return; 18238a1b9b6aSSam Leffler } 18248a1b9b6aSSam Leffler /* 18258a1b9b6aSSam Leffler * Tag the frame with it's expiry time and insert 18268a1b9b6aSSam Leffler * it in the queue. The aging interval is 4 times 18278a1b9b6aSSam Leffler * the listen interval specified by the station. 18288a1b9b6aSSam Leffler * Frames that sit around too long are reclaimed 18298a1b9b6aSSam Leffler * using this information. 18308a1b9b6aSSam Leffler */ 18318a1b9b6aSSam Leffler /* XXX handle overflow? */ 1832d365f9c7SSam Leffler age = ((ni->ni_intval * ic->ic_bintval) << 2) / 1024; /* TU -> secs */ 18338a1b9b6aSSam Leffler _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age); 18348a1b9b6aSSam Leffler IEEE80211_NODE_SAVEQ_UNLOCK(ni); 18358a1b9b6aSSam Leffler 18368a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, 1837d365f9c7SSam Leffler "[%s] save frame with age %d, %u now queued\n", 1838d365f9c7SSam Leffler ether_sprintf(ni->ni_macaddr), age, qlen); 18398a1b9b6aSSam Leffler 18408a1b9b6aSSam Leffler if (qlen == 1) 1841edfa57d0SSam Leffler ic->ic_set_tim(ni, 1); 18428a1b9b6aSSam Leffler } 1843