ieee80211_output.c revision 116742
1109998Smarkm/*- 2109998Smarkm * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 3109998Smarkm * All rights reserved. 4109998Smarkm * 5109998Smarkm * Redistribution and use in source and binary forms, with or without 6109998Smarkm * modification, are permitted provided that the following conditions 7109998Smarkm * are met: 8109998Smarkm * 1. Redistributions of source code must retain the above copyright 9109998Smarkm * notice, this list of conditions and the following disclaimer, 10280297Sjkim * without modification. 11109998Smarkm * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12109998Smarkm * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13109998Smarkm * redistribution must be conditioned upon including a substantially 14109998Smarkm * similar Disclaimer requirement for further binary redistribution. 15109998Smarkm * 3. Neither the names of the above-listed copyright holders nor the names 16109998Smarkm * of any contributors may be used to endorse or promote products derived 17109998Smarkm * from this software without specific prior written permission. 18109998Smarkm * 19109998Smarkm * Alternatively, this software may be distributed under the terms of the 20109998Smarkm * GNU General Public License ("GPL") version 2 as published by the Free 21109998Smarkm * Software Foundation. 22109998Smarkm * 23109998Smarkm * NO WARRANTY 24109998Smarkm * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25109998Smarkm * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26109998Smarkm * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 27109998Smarkm * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 28109998Smarkm * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 29109998Smarkm * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30109998Smarkm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31109998Smarkm * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 32109998Smarkm * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33109998Smarkm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 34109998Smarkm * THE POSSIBILITY OF SUCH DAMAGES. 35109998Smarkm */ 36109998Smarkm 37109998Smarkm#include <sys/cdefs.h> 38109998Smarkm__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_output.c 116742 2003-06-23 16:55:01Z sam $"); 39109998Smarkm 40109998Smarkm#include "opt_inet.h" 41109998Smarkm 42109998Smarkm#include <sys/param.h> 43109998Smarkm#include <sys/systm.h> 44109998Smarkm#include <sys/mbuf.h> 45109998Smarkm#include <sys/malloc.h> 46109998Smarkm#include <sys/kernel.h> 47109998Smarkm#include <sys/socket.h> 48109998Smarkm#include <sys/sockio.h> 49109998Smarkm#include <sys/endian.h> 50109998Smarkm#include <sys/errno.h> 51109998Smarkm#include <sys/bus.h> 52109998Smarkm#include <sys/proc.h> 53109998Smarkm#include <sys/sysctl.h> 54109998Smarkm 55280297Sjkim#include <machine/atomic.h> 56280297Sjkim 57280297Sjkim#include <net/if.h> 58280297Sjkim#include <net/if_dl.h> 59280297Sjkim#include <net/if_media.h> 60109998Smarkm#include <net/if_arp.h> 61109998Smarkm#include <net/ethernet.h> 62109998Smarkm#include <net/if_llc.h> 63109998Smarkm 64280297Sjkim#include <net80211/ieee80211_var.h> 65109998Smarkm 66280297Sjkim#include <net/bpf.h> 67280297Sjkim 68280297Sjkim#ifdef INET 69280297Sjkim#include <netinet/in.h> 70280297Sjkim#include <netinet/if_ether.h> 71280297Sjkim#endif 72280297Sjkim 73109998Smarkmint 74109998Smarkmieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, 75280297Sjkim struct mbuf *m, int type) 76280297Sjkim{ 77109998Smarkm struct ieee80211com *ic = (void *)ifp; 78280297Sjkim struct ieee80211_frame *wh; 79280297Sjkim 80109998Smarkm /* XXX this probably shouldn't be permitted */ 81109998Smarkm KASSERT(ni != NULL, ("%s: null node", __func__)); 82109998Smarkm ni->ni_inact = 0; 83109998Smarkm 84109998Smarkm M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 85109998Smarkm if (m == NULL) 86280297Sjkim return ENOMEM; 87109998Smarkm wh = mtod(m, struct ieee80211_frame *); 88109998Smarkm wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; 89280297Sjkim wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 90280297Sjkim *(u_int16_t *)wh->i_dur = 0; 91280297Sjkim *(u_int16_t *)wh->i_seq = 92109998Smarkm htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); 93109998Smarkm ni->ni_txseq++; 94280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 95280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 96280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 97280297Sjkim 98280297Sjkim if (ifp->if_flags & IFF_DEBUG) { 99109998Smarkm /* avoid to print too many frames */ 100280297Sjkim if (ic->ic_opmode == IEEE80211_M_IBSS || 101280297Sjkim#ifdef IEEE80211_DEBUG 102280297Sjkim ieee80211_debug > 1 || 103280297Sjkim#endif 104280297Sjkim (type & IEEE80211_FC0_SUBTYPE_MASK) != 105280297Sjkim IEEE80211_FC0_SUBTYPE_PROBE_RESP) 106280297Sjkim if_printf(ifp, "sending %s to %s on channel %u\n", 107280297Sjkim ieee80211_mgt_subtype_name[ 108280297Sjkim (type & IEEE80211_FC0_SUBTYPE_MASK) 109280297Sjkim >> IEEE80211_FC0_SUBTYPE_SHIFT], 110280297Sjkim ether_sprintf(ni->ni_macaddr), 111280297Sjkim ieee80211_chan2ieee(ic, ni->ni_chan)); 112280297Sjkim } 113109998Smarkm IF_ENQUEUE(&ic->ic_mgtq, m); 114280297Sjkim ifp->if_timer = 1; 115109998Smarkm (*ifp->if_start)(ifp); 116109998Smarkm return 0; 117280297Sjkim} 118109998Smarkm 119109998Smarkmstruct mbuf * 120280297Sjkimieee80211_encap(struct ifnet *ifp, struct mbuf *m) 121280297Sjkim{ 122280297Sjkim struct ieee80211com *ic = (void *)ifp; 123109998Smarkm struct ether_header eh; 124109998Smarkm struct ieee80211_frame *wh; 125109998Smarkm struct llc *llc; 126109998Smarkm struct ieee80211_node *ni; 127280297Sjkim 128109998Smarkm if (m->m_len < sizeof(struct ether_header)) { 129280297Sjkim m = m_pullup(m, sizeof(struct ether_header)); 130280297Sjkim if (m == NULL) 131280297Sjkim return NULL; 132280297Sjkim } 133280297Sjkim memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); 134109998Smarkm 135280297Sjkim ni = ieee80211_find_node(ic, eh.ether_dhost); 136306195Sjkim if (ni == NULL) /*ic_opmode?? XXX*/ 137306195Sjkim ni = ieee80211_ref_node(ic->ic_bss); 138306195Sjkim ni->ni_inact = 0; 139306195Sjkim 140109998Smarkm m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 141109998Smarkm llc = mtod(m, struct llc *); 142109998Smarkm llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 143280297Sjkim llc->llc_control = LLC_UI; 144109998Smarkm llc->llc_snap.org_code[0] = 0; 145280297Sjkim llc->llc_snap.org_code[1] = 0; 146280297Sjkim llc->llc_snap.org_code[2] = 0; 147280297Sjkim llc->llc_snap.ether_type = eh.ether_type; 148280297Sjkim M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 149280297Sjkim if (m == NULL) { 150280297Sjkim ieee80211_unref_node(&ni); 151280297Sjkim return NULL; 152109998Smarkm } 153280297Sjkim wh = mtod(m, struct ieee80211_frame *); 154109998Smarkm wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 155280297Sjkim *(u_int16_t *)wh->i_dur = 0; 156280297Sjkim *(u_int16_t *)wh->i_seq = 157280297Sjkim htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); 158109998Smarkm ni->ni_txseq++; 159109998Smarkm switch (ic->ic_opmode) { 160109998Smarkm case IEEE80211_M_STA: 161280297Sjkim wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 162280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); 163109998Smarkm IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 164109998Smarkm IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 165109998Smarkm break; 166109998Smarkm case IEEE80211_M_IBSS: 167280297Sjkim case IEEE80211_M_AHDEMO: 168280297Sjkim wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 169280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 170280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 171280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 172280297Sjkim break; 173280297Sjkim case IEEE80211_M_HOSTAP: 174280297Sjkim wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 175280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 176280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); 177280297Sjkim IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 178109998Smarkm break; 179280297Sjkim } 180280297Sjkim ieee80211_unref_node(&ni); 181109998Smarkm return m; 182280297Sjkim} 183280297Sjkim 184280297Sjkim/* 185280297Sjkim * Add a supported rates element id to a frame. 186280297Sjkim */ 187109998Smarkmu_int8_t * 188280297Sjkimieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) 189280297Sjkim{ 190109998Smarkm int nrates; 191280297Sjkim 192280297Sjkim *frm++ = IEEE80211_ELEMID_RATES; 193306195Sjkim nrates = rs->rs_nrates; 194306195Sjkim if (nrates > IEEE80211_RATE_SIZE) 195306195Sjkim nrates = IEEE80211_RATE_SIZE; 196306195Sjkim *frm++ = nrates; 197306195Sjkim memcpy(frm, rs->rs_rates, nrates); 198280297Sjkim return frm + nrates; 199280297Sjkim} 200280297Sjkim 201109998Smarkm/* 202280297Sjkim * Add an extended supported rates element id to a frame. 203280297Sjkim */ 204280297Sjkimu_int8_t * 205280297Sjkimieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) 206109998Smarkm{ 207280297Sjkim /* 208109998Smarkm * Add an extended supported rates element if operating in 11g mode. 209280297Sjkim */ 210280297Sjkim if (rs->rs_nrates > IEEE80211_RATE_SIZE) { 211280297Sjkim int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 212280297Sjkim *frm++ = IEEE80211_ELEMID_XRATES; 213280297Sjkim *frm++ = nrates; 214280297Sjkim memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 215109998Smarkm frm += nrates; 216280297Sjkim } 217280297Sjkim return frm; 218280297Sjkim} 219280297Sjkim 220280297Sjkim/* 221280297Sjkim * Add an ssid elemet to a frame. 222280297Sjkim */ 223280297Sjkimstatic u_int8_t * 224280297Sjkimieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) 225280297Sjkim{ 226280297Sjkim *frm++ = IEEE80211_ELEMID_SSID; 227280297Sjkim *frm++ = len; 228280297Sjkim memcpy(frm, ssid, len); 229280297Sjkim return frm + len; 230280297Sjkim} 231280297Sjkim 232109998Smarkmstatic struct mbuf * 233280297Sjkimieee80211_getmbuf(int flags, int type, u_int pktlen) 234109998Smarkm{ 235109998Smarkm struct mbuf *m; 236280297Sjkim 237109998Smarkm if (pktlen > MHLEN) 238109998Smarkm MGETHDR(m, flags, type); 239280297Sjkim else 240280297Sjkim m = m_getcl(flags, type, M_PKTHDR); 241280297Sjkim return m; 242280297Sjkim} 243280297Sjkim 244109998Smarkmint 245280297Sjkimieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, 246109998Smarkm int type, int arg) 247109998Smarkm{ 248280297Sjkim struct ifnet *ifp = &ic->ic_if; 249280297Sjkim struct mbuf *m; 250280297Sjkim u_int8_t *frm; 251109998Smarkm enum ieee80211_phymode mode; 252109998Smarkm u_int16_t capinfo; 253280297Sjkim int ret, timer; 254280297Sjkim 255280297Sjkim timer = 0; 256280297Sjkim switch (type) { 257280297Sjkim case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 258109998Smarkm /* 259280297Sjkim * prreq frame format 260109998Smarkm * [tlv] ssid 261280297Sjkim * [tlv] supported rates 262109998Smarkm * [tlv] extended supported rates 263109998Smarkm */ 264109998Smarkm m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 265109998Smarkm 2 + ic->ic_des_esslen 266109998Smarkm + 2 + IEEE80211_RATE_SIZE 267109998Smarkm + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 268109998Smarkm if (m == NULL) 269109998Smarkm return ENOMEM; 270280297Sjkim m->m_data += sizeof(struct ieee80211_frame); 271109998Smarkm frm = mtod(m, u_int8_t *); 272109998Smarkm frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); 273280297Sjkim mode = ieee80211_chan2mode(ic, ni->ni_chan); 274280297Sjkim frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); 275280297Sjkim frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); 276109998Smarkm m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 277280297Sjkim 278109998Smarkm timer = IEEE80211_TRANS_WAIT; 279109998Smarkm break; 280109998Smarkm 281109998Smarkm case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 282280297Sjkim /* 283109998Smarkm * probe response frame format 284280297Sjkim * [8] time stamp 285280297Sjkim * [2] beacon interval 286280297Sjkim * [2] cabability information 287280297Sjkim * [tlv] ssid 288109998Smarkm * [tlv] supported rates 289280297Sjkim * [tlv] parameter set (IBSS) 290280297Sjkim * [tlv] extended supported rates 291109998Smarkm */ 292280297Sjkim m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 293109998Smarkm 8 + 2 + 2 + 2 294280297Sjkim + 2 + ni->ni_esslen 295280297Sjkim + 2 + IEEE80211_RATE_SIZE 296280297Sjkim + 6 297280297Sjkim + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 298109998Smarkm if (m == NULL) 299280297Sjkim return ENOMEM; 300109998Smarkm m->m_data += sizeof(struct ieee80211_frame); 301109998Smarkm frm = mtod(m, u_int8_t *); 302280297Sjkim 303280297Sjkim memset(frm, 0, 8); /* timestamp should be filled later */ 304280297Sjkim frm += 8; 305280297Sjkim *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); 306280297Sjkim frm += 2; 307109998Smarkm if (ic->ic_opmode == IEEE80211_M_IBSS) 308109998Smarkm capinfo = IEEE80211_CAPINFO_IBSS; 309280297Sjkim else 310109998Smarkm capinfo = IEEE80211_CAPINFO_ESS; 311109998Smarkm if (ic->ic_flags & IEEE80211_F_WEPON) 312109998Smarkm capinfo |= IEEE80211_CAPINFO_PRIVACY; 313109998Smarkm *(u_int16_t *)frm = htole16(capinfo); 314280297Sjkim frm += 2; 315280297Sjkim 316280297Sjkim frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, 317109998Smarkm ic->ic_bss->ni_esslen); 318280297Sjkim frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); 319280297Sjkim 320109998Smarkm if (ic->ic_opmode == IEEE80211_M_IBSS) { 321280297Sjkim *frm++ = IEEE80211_ELEMID_IBSSPARMS; 322280297Sjkim *frm++ = 2; 323280297Sjkim *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 324280297Sjkim } else { /* IEEE80211_M_HOSTAP */ 325109998Smarkm /* TODO: TIM */ 326109998Smarkm *frm++ = IEEE80211_ELEMID_TIM; 327280297Sjkim *frm++ = 4; /* length */ 328280297Sjkim *frm++ = 0; /* DTIM count */ 329280297Sjkim *frm++ = 1; /* DTIM period */ 330280297Sjkim *frm++ = 0; /* bitmap control */ 331109998Smarkm *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ 332280297Sjkim } 333280297Sjkim frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); 334280297Sjkim m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 335280297Sjkim break; 336280297Sjkim 337280297Sjkim case IEEE80211_FC0_SUBTYPE_AUTH: 338280297Sjkim MGETHDR(m, M_DONTWAIT, MT_DATA); 339280297Sjkim if (m == NULL) 340109998Smarkm return ENOMEM; 341280297Sjkim MH_ALIGN(m, 2 * 3); 342280297Sjkim m->m_pkthdr.len = m->m_len = 6; 343280297Sjkim frm = mtod(m, u_int8_t *); 344109998Smarkm /* TODO: shared key auth */ 345280297Sjkim ((u_int16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN); 346306195Sjkim ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ 347306195Sjkim ((u_int16_t *)frm)[2] = 0; /* status */ 348306195Sjkim if (ic->ic_opmode == IEEE80211_M_STA) 349306195Sjkim timer = IEEE80211_TRANS_WAIT; 350306195Sjkim break; 351280297Sjkim 352280297Sjkim case IEEE80211_FC0_SUBTYPE_DEAUTH: 353280297Sjkim if (ifp->if_flags & IFF_DEBUG) 354109998Smarkm if_printf(ifp, "station %s deauthenticate (reason %d)\n", 355280297Sjkim ether_sprintf(ni->ni_macaddr), arg); 356280297Sjkim MGETHDR(m, M_DONTWAIT, MT_DATA); 357280297Sjkim if (m == NULL) 358280297Sjkim return ENOMEM; 359280297Sjkim MH_ALIGN(m, 2); 360280297Sjkim m->m_pkthdr.len = m->m_len = 2; 361280297Sjkim *mtod(m, u_int16_t *) = htole16(arg); /* reason */ 362280297Sjkim break; 363280297Sjkim 364280297Sjkim case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 365280297Sjkim case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 366280297Sjkim /* 367280297Sjkim * asreq frame format 368109998Smarkm * [2] capability information 369109998Smarkm * [2] listen interval 370280297Sjkim * [6*] current AP address (reassoc only) 371109998Smarkm * [tlv] ssid 372280297Sjkim * [tlv] supported rates 373280297Sjkim * [tlv] extended supported rates 374280297Sjkim */ 375280297Sjkim m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 376109998Smarkm sizeof(capinfo) 377280297Sjkim + sizeof(u_int16_t) 378280297Sjkim + IEEE80211_ADDR_LEN 379109998Smarkm + 2 + ni->ni_esslen 380280297Sjkim + 2 + IEEE80211_RATE_SIZE 381306195Sjkim + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 382306195Sjkim if (m == NULL) 383306195Sjkim return ENOMEM; 384306195Sjkim m->m_data += sizeof(struct ieee80211_frame); 385280297Sjkim frm = mtod(m, u_int8_t *); 386280297Sjkim 387109998Smarkm capinfo = 0; 388109998Smarkm if (ic->ic_opmode == IEEE80211_M_IBSS) 389280297Sjkim capinfo |= IEEE80211_CAPINFO_IBSS; 390109998Smarkm else /* IEEE80211_M_STA */ 391280297Sjkim capinfo |= IEEE80211_CAPINFO_ESS; 392280297Sjkim if (ic->ic_flags & IEEE80211_F_WEPON) 393109998Smarkm capinfo |= IEEE80211_CAPINFO_PRIVACY; 394280297Sjkim if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 395109998Smarkm capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 396280297Sjkim if (ic->ic_flags & IEEE80211_F_SHSLOT) 397280297Sjkim capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 398280297Sjkim *(u_int16_t *)frm = htole16(capinfo); 399280297Sjkim frm += 2; 400280297Sjkim 401280297Sjkim *(u_int16_t *)frm = htole16(ic->ic_lintval); 402280297Sjkim frm += 2; 403280297Sjkim 404280297Sjkim if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 405109998Smarkm IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); 406109998Smarkm frm += IEEE80211_ADDR_LEN; 407280297Sjkim } 408109998Smarkm 409280297Sjkim frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 410280297Sjkim frm = ieee80211_add_rates(frm, &ni->ni_rates); 411280297Sjkim frm = ieee80211_add_xrates(frm, &ni->ni_rates); 412280297Sjkim m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 413109998Smarkm 414109998Smarkm timer = IEEE80211_TRANS_WAIT; 415280297Sjkim break; 416109998Smarkm 417280297Sjkim case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 418306195Sjkim case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 419306195Sjkim /* 420306195Sjkim * asreq frame format 421306195Sjkim * [2] capability information 422280297Sjkim * [2] status 423109998Smarkm * [2] association ID 424109998Smarkm * [tlv] supported rates 425280297Sjkim * [tlv] extended supported rates 426109998Smarkm */ 427109998Smarkm m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, 428280297Sjkim sizeof(capinfo) 429109998Smarkm + sizeof(u_int16_t) 430280297Sjkim + sizeof(u_int16_t) 431109998Smarkm + 2 + IEEE80211_RATE_SIZE 432280297Sjkim + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); 433109998Smarkm if (m == NULL) 434109998Smarkm return ENOMEM; 435109998Smarkm m->m_data += sizeof(struct ieee80211_frame); 436280297Sjkim frm = mtod(m, u_int8_t *); 437109998Smarkm 438109998Smarkm capinfo = IEEE80211_CAPINFO_ESS; 439109998Smarkm if (ic->ic_flags & IEEE80211_F_WEPON) 440109998Smarkm capinfo |= IEEE80211_CAPINFO_PRIVACY; 441109998Smarkm *(u_int16_t *)frm = htole16(capinfo); 442109998Smarkm frm += 2; 443109998Smarkm 444109998Smarkm *(u_int16_t *)frm = htole16(arg); /* status */ 445280297Sjkim frm += 2; 446109998Smarkm 447109998Smarkm if (arg == IEEE80211_STATUS_SUCCESS && ni != NULL) 448280297Sjkim *(u_int16_t *)frm = htole16(ni->ni_associd); 449280297Sjkim else 450280297Sjkim *(u_int16_t *)frm = htole16(0); 451109998Smarkm frm += 2; 452280297Sjkim 453109998Smarkm if (ni != NULL) { 454 frm = ieee80211_add_rates(frm, &ni->ni_rates); 455 frm = ieee80211_add_xrates(frm, &ni->ni_rates); 456 } else { 457 frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); 458 frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); 459 } 460 m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 461 break; 462 463 case IEEE80211_FC0_SUBTYPE_DISASSOC: 464 if (ifp->if_flags & IFF_DEBUG) 465 if_printf(ifp, "station %s disassociate (reason %d)\n", 466 ether_sprintf(ni->ni_macaddr), arg); 467 MGETHDR(m, M_DONTWAIT, MT_DATA); 468 if (m == NULL) 469 return ENOMEM; 470 MH_ALIGN(m, 2); 471 m->m_pkthdr.len = m->m_len = 2; 472 *mtod(m, u_int16_t *) = htole16(arg); /* reason */ 473 break; 474 475 default: 476 IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n", 477 __func__, type)); 478 return EINVAL; 479 } 480 481 ret = ieee80211_mgmt_output(ifp, ni, m, type); 482 if (ret == 0 && timer) 483 ic->ic_mgt_timer = timer; 484 return ret; 485} 486