11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 31a1e1d21SSam Leffler * Copyright (c) 2002, 2003 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/malloc.h> 421a1e1d21SSam Leffler #include <sys/kernel.h> 431a1e1d21SSam Leffler #include <sys/socket.h> 441a1e1d21SSam Leffler #include <sys/sockio.h> 451a1e1d21SSam Leffler #include <sys/endian.h> 461a1e1d21SSam Leffler #include <sys/errno.h> 471a1e1d21SSam Leffler #include <sys/bus.h> 481a1e1d21SSam Leffler #include <sys/proc.h> 491a1e1d21SSam Leffler #include <sys/sysctl.h> 501a1e1d21SSam Leffler 511a1e1d21SSam Leffler #include <machine/atomic.h> 521a1e1d21SSam Leffler 531a1e1d21SSam Leffler #include <net/if.h> 541a1e1d21SSam Leffler #include <net/if_dl.h> 551a1e1d21SSam Leffler #include <net/if_media.h> 561a1e1d21SSam Leffler #include <net/if_arp.h> 571a1e1d21SSam Leffler #include <net/ethernet.h> 581a1e1d21SSam Leffler #include <net/if_llc.h> 591a1e1d21SSam Leffler 601a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 611a1e1d21SSam Leffler 621a1e1d21SSam Leffler #include <net/bpf.h> 631a1e1d21SSam Leffler 641a1e1d21SSam Leffler #ifdef INET 651a1e1d21SSam Leffler #include <netinet/in.h> 661a1e1d21SSam Leffler #include <netinet/if_ether.h> 671a1e1d21SSam Leffler #endif 681a1e1d21SSam Leffler 691a1e1d21SSam Leffler int 701a1e1d21SSam Leffler ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, 711a1e1d21SSam Leffler struct mbuf *m, int type) 721a1e1d21SSam Leffler { 731a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 741a1e1d21SSam Leffler struct ieee80211_frame *wh; 751a1e1d21SSam Leffler 761a1e1d21SSam Leffler /* XXX this probably shouldn't be permitted */ 771a1e1d21SSam Leffler KASSERT(ni != NULL, ("%s: null node", __func__)); 781a1e1d21SSam Leffler ni->ni_inact = 0; 791a1e1d21SSam Leffler 801a1e1d21SSam Leffler M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 811a1e1d21SSam Leffler if (m == NULL) 821a1e1d21SSam Leffler return ENOMEM; 831a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 841a1e1d21SSam Leffler wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; 851a1e1d21SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 861a1e1d21SSam Leffler *(u_int16_t *)wh->i_dur = 0; 871a1e1d21SSam Leffler *(u_int16_t *)wh->i_seq = 881a1e1d21SSam Leffler htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); 891a1e1d21SSam Leffler ni->ni_txseq++; 901a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 911a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 921a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 931a1e1d21SSam Leffler 941a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) { 951a1e1d21SSam Leffler /* avoid to print too many frames */ 961a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS || 971a1e1d21SSam Leffler #ifdef IEEE80211_DEBUG 981a1e1d21SSam Leffler ieee80211_debug > 1 || 991a1e1d21SSam Leffler #endif 1001a1e1d21SSam Leffler (type & IEEE80211_FC0_SUBTYPE_MASK) != 1011a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_PROBE_RESP) 1021a1e1d21SSam Leffler if_printf(ifp, "sending %s to %s on channel %u\n", 1031a1e1d21SSam Leffler ieee80211_mgt_subtype_name[ 1041a1e1d21SSam Leffler (type & IEEE80211_FC0_SUBTYPE_MASK) 1051a1e1d21SSam Leffler >> IEEE80211_FC0_SUBTYPE_SHIFT], 1061a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), 1071a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ni->ni_chan)); 1081a1e1d21SSam Leffler } 1091a1e1d21SSam Leffler IF_ENQUEUE(&ic->ic_mgtq, m); 1101a1e1d21SSam Leffler ifp->if_timer = 1; 1111a1e1d21SSam Leffler (*ifp->if_start)(ifp); 1121a1e1d21SSam Leffler return 0; 1131a1e1d21SSam Leffler } 1141a1e1d21SSam Leffler 1151a1e1d21SSam Leffler struct mbuf * 1161a1e1d21SSam Leffler ieee80211_encap(struct ifnet *ifp, struct mbuf *m) 1171a1e1d21SSam Leffler { 1181a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 1191a1e1d21SSam Leffler struct ether_header eh; 1201a1e1d21SSam Leffler struct ieee80211_frame *wh; 1211a1e1d21SSam Leffler struct llc *llc; 1221a1e1d21SSam Leffler struct ieee80211_node *ni; 1231a1e1d21SSam Leffler 1241a1e1d21SSam Leffler if (m->m_len < sizeof(struct ether_header)) { 1251a1e1d21SSam Leffler m = m_pullup(m, sizeof(struct ether_header)); 1261a1e1d21SSam Leffler if (m == NULL) 1271a1e1d21SSam Leffler return NULL; 1281a1e1d21SSam Leffler } 1291a1e1d21SSam Leffler memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); 1301a1e1d21SSam Leffler 1311a1e1d21SSam Leffler ni = ieee80211_find_node(ic, eh.ether_dhost); 1321a1e1d21SSam Leffler if (ni == NULL) /*ic_opmode?? XXX*/ 1331a1e1d21SSam Leffler ni = ieee80211_ref_node(ic->ic_bss); 1341a1e1d21SSam Leffler ni->ni_inact = 0; 1351a1e1d21SSam Leffler 1361a1e1d21SSam Leffler m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 1371a1e1d21SSam Leffler llc = mtod(m, struct llc *); 1381a1e1d21SSam Leffler llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 1391a1e1d21SSam Leffler llc->llc_control = LLC_UI; 1401a1e1d21SSam Leffler llc->llc_snap.org_code[0] = 0; 1411a1e1d21SSam Leffler llc->llc_snap.org_code[1] = 0; 1421a1e1d21SSam Leffler llc->llc_snap.org_code[2] = 0; 1431a1e1d21SSam Leffler llc->llc_snap.ether_type = eh.ether_type; 1441a1e1d21SSam Leffler M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 1451a1e1d21SSam Leffler if (m == NULL) { 1461a1e1d21SSam Leffler ieee80211_unref_node(&ni); 1471a1e1d21SSam Leffler return NULL; 1481a1e1d21SSam Leffler } 1491a1e1d21SSam Leffler wh = mtod(m, struct ieee80211_frame *); 1501a1e1d21SSam Leffler wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 1511a1e1d21SSam Leffler *(u_int16_t *)wh->i_dur = 0; 1521a1e1d21SSam Leffler *(u_int16_t *)wh->i_seq = 1531a1e1d21SSam Leffler htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); 1541a1e1d21SSam Leffler ni->ni_txseq++; 1551a1e1d21SSam Leffler switch (ic->ic_opmode) { 1561a1e1d21SSam Leffler case IEEE80211_M_STA: 1571a1e1d21SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 1581a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); 1591a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 1601a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 1611a1e1d21SSam Leffler break; 1621a1e1d21SSam Leffler case IEEE80211_M_IBSS: 1631a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 1641a1e1d21SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 1651a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1661a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 1671a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 1681a1e1d21SSam Leffler break; 1691a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 1701a1e1d21SSam Leffler wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 1711a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1721a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); 1731a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 1741a1e1d21SSam Leffler break; 1752bbe529dSSam Leffler case IEEE80211_M_MONITOR: 1762bbe529dSSam Leffler m_freem(m), m = NULL; 1772bbe529dSSam Leffler break; 1781a1e1d21SSam Leffler } 1791a1e1d21SSam Leffler ieee80211_unref_node(&ni); 1801a1e1d21SSam Leffler return m; 1811a1e1d21SSam Leffler } 1821a1e1d21SSam Leffler 1831a1e1d21SSam Leffler /* 1841a1e1d21SSam Leffler * Add a supported rates element id to a frame. 1851a1e1d21SSam Leffler */ 1861a1e1d21SSam Leffler u_int8_t * 1871a1e1d21SSam Leffler ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) 1881a1e1d21SSam Leffler { 1891a1e1d21SSam Leffler int nrates; 1901a1e1d21SSam Leffler 1911a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_RATES; 1921a1e1d21SSam Leffler nrates = rs->rs_nrates; 1931a1e1d21SSam Leffler if (nrates > IEEE80211_RATE_SIZE) 1941a1e1d21SSam Leffler nrates = IEEE80211_RATE_SIZE; 1951a1e1d21SSam Leffler *frm++ = nrates; 1961a1e1d21SSam Leffler memcpy(frm, rs->rs_rates, nrates); 1971a1e1d21SSam Leffler return frm + nrates; 1981a1e1d21SSam Leffler } 1991a1e1d21SSam Leffler 2001a1e1d21SSam Leffler /* 2011a1e1d21SSam Leffler * Add an extended supported rates element id to a frame. 2021a1e1d21SSam Leffler */ 2031a1e1d21SSam Leffler u_int8_t * 2041a1e1d21SSam Leffler ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) 2051a1e1d21SSam Leffler { 2061a1e1d21SSam Leffler /* 2071a1e1d21SSam Leffler * Add an extended supported rates element if operating in 11g mode. 2081a1e1d21SSam Leffler */ 2091a1e1d21SSam Leffler if (rs->rs_nrates > IEEE80211_RATE_SIZE) { 2101a1e1d21SSam Leffler int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 2111a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_XRATES; 2121a1e1d21SSam Leffler *frm++ = nrates; 2131a1e1d21SSam Leffler memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 2141a1e1d21SSam Leffler frm += nrates; 2151a1e1d21SSam Leffler } 2161a1e1d21SSam Leffler return frm; 2171a1e1d21SSam Leffler } 2181a1e1d21SSam Leffler 2191a1e1d21SSam Leffler /* 2201a1e1d21SSam Leffler * Add an ssid elemet to a frame. 2211a1e1d21SSam Leffler */ 2221a1e1d21SSam Leffler static u_int8_t * 2231a1e1d21SSam Leffler ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) 2241a1e1d21SSam Leffler { 2251a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_SSID; 2261a1e1d21SSam Leffler *frm++ = len; 2271a1e1d21SSam Leffler memcpy(frm, ssid, len); 2281a1e1d21SSam Leffler return frm + len; 2291a1e1d21SSam Leffler } 2301a1e1d21SSam Leffler 2311a1e1d21SSam Leffler static struct mbuf * 2321a1e1d21SSam Leffler ieee80211_getmbuf(int flags, int type, u_int pktlen) 2331a1e1d21SSam Leffler { 2341a1e1d21SSam Leffler struct mbuf *m; 2351a1e1d21SSam Leffler 2361a1e1d21SSam Leffler if (pktlen > MHLEN) 2371a1e1d21SSam Leffler MGETHDR(m, flags, type); 2381a1e1d21SSam Leffler else 2391a1e1d21SSam Leffler m = m_getcl(flags, type, M_PKTHDR); 2401a1e1d21SSam Leffler return m; 2411a1e1d21SSam Leffler } 2421a1e1d21SSam Leffler 2431a1e1d21SSam Leffler int 2441a1e1d21SSam Leffler ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, 2451a1e1d21SSam Leffler int type, int arg) 2461a1e1d21SSam Leffler { 2471a1e1d21SSam Leffler struct ifnet *ifp = &ic->ic_if; 2481a1e1d21SSam Leffler struct mbuf *m; 2491a1e1d21SSam Leffler u_int8_t *frm; 2501a1e1d21SSam Leffler enum ieee80211_phymode mode; 2511a1e1d21SSam Leffler u_int16_t capinfo; 2521a1e1d21SSam Leffler int ret, timer; 2531a1e1d21SSam Leffler 2541a1e1d21SSam Leffler timer = 0; 2551a1e1d21SSam Leffler switch (type) { 2561a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 2571a1e1d21SSam Leffler /* 2581a1e1d21SSam Leffler * prreq frame format 2591a1e1d21SSam Leffler * [tlv] ssid 2601a1e1d21SSam Leffler * [tlv] supported rates 2611a1e1d21SSam Leffler * [tlv] extended supported rates 2621a1e1d21SSam Leffler */ 2631a1e1d21SSam Leffler m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 2641a1e1d21SSam Leffler 2 + ic->ic_des_esslen 2651a1e1d21SSam Leffler + 2 + IEEE80211_RATE_SIZE 2661a1e1d21SSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 2671a1e1d21SSam Leffler if (m == NULL) 2681a1e1d21SSam Leffler return ENOMEM; 2691a1e1d21SSam Leffler m->m_data += sizeof(struct ieee80211_frame); 2701a1e1d21SSam Leffler frm = mtod(m, u_int8_t *); 2711a1e1d21SSam Leffler frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); 2721a1e1d21SSam Leffler mode = ieee80211_chan2mode(ic, ni->ni_chan); 2731a1e1d21SSam Leffler frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); 2741a1e1d21SSam Leffler frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); 2751a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 2761a1e1d21SSam Leffler 2771a1e1d21SSam Leffler timer = IEEE80211_TRANS_WAIT; 2781a1e1d21SSam Leffler break; 2791a1e1d21SSam Leffler 2801a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 2811a1e1d21SSam Leffler /* 2821a1e1d21SSam Leffler * probe response frame format 2831a1e1d21SSam Leffler * [8] time stamp 2841a1e1d21SSam Leffler * [2] beacon interval 2851a1e1d21SSam Leffler * [2] cabability information 2861a1e1d21SSam Leffler * [tlv] ssid 2871a1e1d21SSam Leffler * [tlv] supported rates 2881a1e1d21SSam Leffler * [tlv] parameter set (IBSS) 2891a1e1d21SSam Leffler * [tlv] extended supported rates 2901a1e1d21SSam Leffler */ 2911a1e1d21SSam Leffler m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 2921a1e1d21SSam Leffler 8 + 2 + 2 + 2 2931a1e1d21SSam Leffler + 2 + ni->ni_esslen 2941a1e1d21SSam Leffler + 2 + IEEE80211_RATE_SIZE 2951a1e1d21SSam Leffler + 6 2961a1e1d21SSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 2971a1e1d21SSam Leffler if (m == NULL) 2981a1e1d21SSam Leffler return ENOMEM; 2991a1e1d21SSam Leffler m->m_data += sizeof(struct ieee80211_frame); 3001a1e1d21SSam Leffler frm = mtod(m, u_int8_t *); 3011a1e1d21SSam Leffler 3021a1e1d21SSam Leffler memset(frm, 0, 8); /* timestamp should be filled later */ 3031a1e1d21SSam Leffler frm += 8; 3041a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); 3051a1e1d21SSam Leffler frm += 2; 3061a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) 3071a1e1d21SSam Leffler capinfo = IEEE80211_CAPINFO_IBSS; 3081a1e1d21SSam Leffler else 3091a1e1d21SSam Leffler capinfo = IEEE80211_CAPINFO_ESS; 3101a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) 3111a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_PRIVACY; 3121a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(capinfo); 3131a1e1d21SSam Leffler frm += 2; 3141a1e1d21SSam Leffler 3151a1e1d21SSam Leffler frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, 3161a1e1d21SSam Leffler ic->ic_bss->ni_esslen); 3171a1e1d21SSam Leffler frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); 3181a1e1d21SSam Leffler 3191a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) { 3201a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_IBSSPARMS; 3211a1e1d21SSam Leffler *frm++ = 2; 3221a1e1d21SSam Leffler *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 3231a1e1d21SSam Leffler } else { /* IEEE80211_M_HOSTAP */ 3241a1e1d21SSam Leffler /* TODO: TIM */ 3251a1e1d21SSam Leffler *frm++ = IEEE80211_ELEMID_TIM; 3261a1e1d21SSam Leffler *frm++ = 4; /* length */ 3271a1e1d21SSam Leffler *frm++ = 0; /* DTIM count */ 3281a1e1d21SSam Leffler *frm++ = 1; /* DTIM period */ 3291a1e1d21SSam Leffler *frm++ = 0; /* bitmap control */ 3301a1e1d21SSam Leffler *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ 3311a1e1d21SSam Leffler } 3321a1e1d21SSam Leffler frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); 3331a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 3341a1e1d21SSam Leffler break; 3351a1e1d21SSam Leffler 3361a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 3371a1e1d21SSam Leffler MGETHDR(m, M_DONTWAIT, MT_DATA); 3381a1e1d21SSam Leffler if (m == NULL) 3391a1e1d21SSam Leffler return ENOMEM; 3401a1e1d21SSam Leffler MH_ALIGN(m, 2 * 3); 3411a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = 6; 3421a1e1d21SSam Leffler frm = mtod(m, u_int8_t *); 3431a1e1d21SSam Leffler /* TODO: shared key auth */ 3441a1e1d21SSam Leffler ((u_int16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN); 3451a1e1d21SSam Leffler ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ 3461a1e1d21SSam Leffler ((u_int16_t *)frm)[2] = 0; /* status */ 3471a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 3481a1e1d21SSam Leffler timer = IEEE80211_TRANS_WAIT; 3491a1e1d21SSam Leffler break; 3501a1e1d21SSam Leffler 3511a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 3521a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 3531a1e1d21SSam Leffler if_printf(ifp, "station %s deauthenticate (reason %d)\n", 3541a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), arg); 3551a1e1d21SSam Leffler MGETHDR(m, M_DONTWAIT, MT_DATA); 3561a1e1d21SSam Leffler if (m == NULL) 3571a1e1d21SSam Leffler return ENOMEM; 3581a1e1d21SSam Leffler MH_ALIGN(m, 2); 3591a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = 2; 3601a1e1d21SSam Leffler *mtod(m, u_int16_t *) = htole16(arg); /* reason */ 3611a1e1d21SSam Leffler break; 3621a1e1d21SSam Leffler 3631a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 3641a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 3651a1e1d21SSam Leffler /* 3661a1e1d21SSam Leffler * asreq frame format 3671a1e1d21SSam Leffler * [2] capability information 3681a1e1d21SSam Leffler * [2] listen interval 3691a1e1d21SSam Leffler * [6*] current AP address (reassoc only) 3701a1e1d21SSam Leffler * [tlv] ssid 3711a1e1d21SSam Leffler * [tlv] supported rates 3721a1e1d21SSam Leffler * [tlv] extended supported rates 3731a1e1d21SSam Leffler */ 3741a1e1d21SSam Leffler m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 3751a1e1d21SSam Leffler sizeof(capinfo) 3761a1e1d21SSam Leffler + sizeof(u_int16_t) 3771a1e1d21SSam Leffler + IEEE80211_ADDR_LEN 3781a1e1d21SSam Leffler + 2 + ni->ni_esslen 3791a1e1d21SSam Leffler + 2 + IEEE80211_RATE_SIZE 3801a1e1d21SSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 3811a1e1d21SSam Leffler if (m == NULL) 3821a1e1d21SSam Leffler return ENOMEM; 3831a1e1d21SSam Leffler m->m_data += sizeof(struct ieee80211_frame); 3841a1e1d21SSam Leffler frm = mtod(m, u_int8_t *); 3851a1e1d21SSam Leffler 3861a1e1d21SSam Leffler capinfo = 0; 3871a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS) 3881a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_IBSS; 3891a1e1d21SSam Leffler else /* IEEE80211_M_STA */ 3901a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_ESS; 3911a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) 3921a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_PRIVACY; 3931a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 3941a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 3951a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_SHSLOT) 3961a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 3971a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(capinfo); 3981a1e1d21SSam Leffler frm += 2; 3991a1e1d21SSam Leffler 4001a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(ic->ic_lintval); 4011a1e1d21SSam Leffler frm += 2; 4021a1e1d21SSam Leffler 4031a1e1d21SSam Leffler if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 4041a1e1d21SSam Leffler IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); 4051a1e1d21SSam Leffler frm += IEEE80211_ADDR_LEN; 4061a1e1d21SSam Leffler } 4071a1e1d21SSam Leffler 4081a1e1d21SSam Leffler frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 4091a1e1d21SSam Leffler frm = ieee80211_add_rates(frm, &ni->ni_rates); 4101a1e1d21SSam Leffler frm = ieee80211_add_xrates(frm, &ni->ni_rates); 4111a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 4121a1e1d21SSam Leffler 4131a1e1d21SSam Leffler timer = IEEE80211_TRANS_WAIT; 4141a1e1d21SSam Leffler break; 4151a1e1d21SSam Leffler 4161a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 4171a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 4181a1e1d21SSam Leffler /* 4191a1e1d21SSam Leffler * asreq frame format 4201a1e1d21SSam Leffler * [2] capability information 4211a1e1d21SSam Leffler * [2] status 4221a1e1d21SSam Leffler * [2] association ID 4231a1e1d21SSam Leffler * [tlv] supported rates 4241a1e1d21SSam Leffler * [tlv] extended supported rates 4251a1e1d21SSam Leffler */ 4261a1e1d21SSam Leffler m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 4271a1e1d21SSam Leffler sizeof(capinfo) 4281a1e1d21SSam Leffler + sizeof(u_int16_t) 4291a1e1d21SSam Leffler + sizeof(u_int16_t) 4301a1e1d21SSam Leffler + 2 + IEEE80211_RATE_SIZE 4311a1e1d21SSam Leffler + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 4321a1e1d21SSam Leffler if (m == NULL) 4331a1e1d21SSam Leffler return ENOMEM; 4341a1e1d21SSam Leffler m->m_data += sizeof(struct ieee80211_frame); 4351a1e1d21SSam Leffler frm = mtod(m, u_int8_t *); 4361a1e1d21SSam Leffler 4371a1e1d21SSam Leffler capinfo = IEEE80211_CAPINFO_ESS; 4381a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) 4391a1e1d21SSam Leffler capinfo |= IEEE80211_CAPINFO_PRIVACY; 4401a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(capinfo); 4411a1e1d21SSam Leffler frm += 2; 4421a1e1d21SSam Leffler 4431a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(arg); /* status */ 4441a1e1d21SSam Leffler frm += 2; 4451a1e1d21SSam Leffler 4461a1e1d21SSam Leffler if (arg == IEEE80211_STATUS_SUCCESS && ni != NULL) 4471a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(ni->ni_associd); 4481a1e1d21SSam Leffler else 4491a1e1d21SSam Leffler *(u_int16_t *)frm = htole16(0); 4501a1e1d21SSam Leffler frm += 2; 4511a1e1d21SSam Leffler 4521a1e1d21SSam Leffler if (ni != NULL) { 4531a1e1d21SSam Leffler frm = ieee80211_add_rates(frm, &ni->ni_rates); 4541a1e1d21SSam Leffler frm = ieee80211_add_xrates(frm, &ni->ni_rates); 4551a1e1d21SSam Leffler } else { 4561a1e1d21SSam Leffler frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); 4571a1e1d21SSam Leffler frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); 4581a1e1d21SSam Leffler } 4591a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 4601a1e1d21SSam Leffler break; 4611a1e1d21SSam Leffler 4621a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DISASSOC: 4631a1e1d21SSam Leffler if (ifp->if_flags & IFF_DEBUG) 4641a1e1d21SSam Leffler if_printf(ifp, "station %s disassociate (reason %d)\n", 4651a1e1d21SSam Leffler ether_sprintf(ni->ni_macaddr), arg); 4661a1e1d21SSam Leffler MGETHDR(m, M_DONTWAIT, MT_DATA); 4671a1e1d21SSam Leffler if (m == NULL) 4681a1e1d21SSam Leffler return ENOMEM; 4691a1e1d21SSam Leffler MH_ALIGN(m, 2); 4701a1e1d21SSam Leffler m->m_pkthdr.len = m->m_len = 2; 4711a1e1d21SSam Leffler *mtod(m, u_int16_t *) = htole16(arg); /* reason */ 4721a1e1d21SSam Leffler break; 4731a1e1d21SSam Leffler 4741a1e1d21SSam Leffler default: 4751a1e1d21SSam Leffler IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n", 4761a1e1d21SSam Leffler __func__, type)); 4771a1e1d21SSam Leffler return EINVAL; 4781a1e1d21SSam Leffler } 4791a1e1d21SSam Leffler 4801a1e1d21SSam Leffler ret = ieee80211_mgmt_output(ifp, ni, m, type); 4811a1e1d21SSam Leffler if (ret == 0 && timer) 4821a1e1d21SSam Leffler ic->ic_mgt_timer = timer; 4831a1e1d21SSam Leffler return ret; 4841a1e1d21SSam Leffler } 485