diff options
author | sam <sam@FreeBSD.org> | 2003-06-23 16:55:01 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2003-06-23 16:55:01 +0000 |
commit | 505adc686a0029ab4aa4877118f251a1894a2603 (patch) | |
tree | 66ac01a331d111073b96a9870f6a5b21af78a8e1 /sys/net80211/ieee80211_output.c | |
parent | 93890f9f0a6aff4f62adc5d1fe890af26121c947 (diff) | |
download | FreeBSD-src-505adc686a0029ab4aa4877118f251a1894a2603.zip FreeBSD-src-505adc686a0029ab4aa4877118f251a1894a2603.tar.gz |
new 802.11 layer:
o code reorg (relative to old netbsd-derived code) for future growth
o drivers now specify available channels and rates and 802.11 layer handles
almost all ifmedia actions
o multi-mode support for 11a/b/g devices
o 11g protocol additions (incomplete)
o new element id additions (for other than 11g)
o node/station table redone for proper locking and to eliminate driver
incestuousness
o split device flags and capabilities to reduce confusion and provide room
for expansion
o incomplete power management infrastructure (need to revisit)
o incomplete hooks for software retry
o more...
Diffstat (limited to 'sys/net80211/ieee80211_output.c')
-rw-r--r-- | sys/net80211/ieee80211_output.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c new file mode 100644 index 0000000..60b4cf0 --- /dev/null +++ b/sys/net80211/ieee80211_output.c @@ -0,0 +1,485 @@ +/*- + * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/bus.h> +#include <sys/proc.h> +#include <sys/sysctl.h> + +#include <machine/atomic.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_llc.h> + +#include <net80211/ieee80211_var.h> + +#include <net/bpf.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +int +ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, + struct mbuf *m, int type) +{ + struct ieee80211com *ic = (void *)ifp; + struct ieee80211_frame *wh; + + /* XXX this probably shouldn't be permitted */ + KASSERT(ni != NULL, ("%s: null node", __func__)); + ni->ni_inact = 0; + + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) + return ENOMEM; + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)wh->i_dur = 0; + *(u_int16_t *)wh->i_seq = + htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseq++; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + + if (ifp->if_flags & IFF_DEBUG) { + /* avoid to print too many frames */ + if (ic->ic_opmode == IEEE80211_M_IBSS || +#ifdef IEEE80211_DEBUG + ieee80211_debug > 1 || +#endif + (type & IEEE80211_FC0_SUBTYPE_MASK) != + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + if_printf(ifp, "sending %s to %s on channel %u\n", + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) + >> IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(ni->ni_macaddr), + ieee80211_chan2ieee(ic, ni->ni_chan)); + } + IF_ENQUEUE(&ic->ic_mgtq, m); + ifp->if_timer = 1; + (*ifp->if_start)(ifp); + return 0; +} + +struct mbuf * +ieee80211_encap(struct ifnet *ifp, struct mbuf *m) +{ + struct ieee80211com *ic = (void *)ifp; + struct ether_header eh; + struct ieee80211_frame *wh; + struct llc *llc; + struct ieee80211_node *ni; + + if (m->m_len < sizeof(struct ether_header)) { + m = m_pullup(m, sizeof(struct ether_header)); + if (m == NULL) + return NULL; + } + memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); + + ni = ieee80211_find_node(ic, eh.ether_dhost); + if (ni == NULL) /*ic_opmode?? XXX*/ + ni = ieee80211_ref_node(ic->ic_bss); + ni->ni_inact = 0; + + m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); + llc = mtod(m, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = 0; + llc->llc_snap.org_code[1] = 0; + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh.ether_type; + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) { + ieee80211_unref_node(&ni); + return NULL; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; + *(u_int16_t *)wh->i_dur = 0; + *(u_int16_t *)wh->i_seq = + htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseq++; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + break; + case IEEE80211_M_HOSTAP: + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); + break; + } + ieee80211_unref_node(&ni); + return m; +} + +/* + * Add a supported rates element id to a frame. + */ +u_int8_t * +ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + int nrates; + + *frm++ = IEEE80211_ELEMID_RATES; + nrates = rs->rs_nrates; + if (nrates > IEEE80211_RATE_SIZE) + nrates = IEEE80211_RATE_SIZE; + *frm++ = nrates; + memcpy(frm, rs->rs_rates, nrates); + return frm + nrates; +} + +/* + * Add an extended supported rates element id to a frame. + */ +u_int8_t * +ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + /* + * Add an extended supported rates element if operating in 11g mode. + */ + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; + *frm++ = IEEE80211_ELEMID_XRATES; + *frm++ = nrates; + memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); + frm += nrates; + } + return frm; +} + +/* + * Add an ssid elemet to a frame. + */ +static u_int8_t * +ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) +{ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = len; + memcpy(frm, ssid, len); + return frm + len; +} + +static struct mbuf * +ieee80211_getmbuf(int flags, int type, u_int pktlen) +{ + struct mbuf *m; + + if (pktlen > MHLEN) + MGETHDR(m, flags, type); + else + m = m_getcl(flags, type, M_PKTHDR); + return m; +} + +int +ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + int type, int arg) +{ + struct ifnet *ifp = &ic->ic_if; + struct mbuf *m; + u_int8_t *frm; + enum ieee80211_phymode mode; + u_int16_t capinfo; + int ret, timer; + + timer = 0; + switch (type) { + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + 2 + ic->ic_des_esslen + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + return ENOMEM; + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); + mode = ieee80211_chan2mode(ic, ni->ni_chan); + frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); + frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + /* + * probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] parameter set (IBSS) + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + 8 + 2 + 2 + 2 + + 2 + ni->ni_esslen + + 2 + IEEE80211_RATE_SIZE + + 6 + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + return ENOMEM; + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + + memset(frm, 0, 8); /* timestamp should be filled later */ + frm += 8; + *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); + frm += 2; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_WEPON) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, + ic->ic_bss->ni_esslen); + frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); + + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + } else { /* IEEE80211_M_HOSTAP */ + /* TODO: TIM */ + *frm++ = IEEE80211_ELEMID_TIM; + *frm++ = 4; /* length */ + *frm++ = 0; /* DTIM count */ + *frm++ = 1; /* DTIM period */ + *frm++ = 0; /* bitmap control */ + *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ + } + frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + break; + + case IEEE80211_FC0_SUBTYPE_AUTH: + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOMEM; + MH_ALIGN(m, 2 * 3); + m->m_pkthdr.len = m->m_len = 6; + frm = mtod(m, u_int8_t *); + /* TODO: shared key auth */ + ((u_int16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN); + ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ + ((u_int16_t *)frm)[2] = 0; /* status */ + if (ic->ic_opmode == IEEE80211_M_STA) + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_DEAUTH: + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s deauthenticate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg); + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOMEM; + MH_ALIGN(m, 2); + m->m_pkthdr.len = m->m_len = 2; + *mtod(m, u_int16_t *) = htole16(arg); /* reason */ + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + sizeof(capinfo) + + sizeof(u_int16_t) + + IEEE80211_ADDR_LEN + + 2 + ni->ni_esslen + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + return ENOMEM; + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + + capinfo = 0; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo |= IEEE80211_CAPINFO_IBSS; + else /* IEEE80211_M_STA */ + capinfo |= IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_WEPON) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + *(u_int16_t *)frm = htole16(ic->ic_lintval); + frm += 2; + + if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); + frm += IEEE80211_ADDR_LEN; + } + + frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); + frm = ieee80211_add_rates(frm, &ni->ni_rates); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + /* + * asreq frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + */ + m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA, + sizeof(capinfo) + + sizeof(u_int16_t) + + sizeof(u_int16_t) + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (m == NULL) + return ENOMEM; + m->m_data += sizeof(struct ieee80211_frame); + frm = mtod(m, u_int8_t *); + + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_WEPON) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + *(u_int16_t *)frm = htole16(arg); /* status */ + frm += 2; + + if (arg == IEEE80211_STATUS_SUCCESS && ni != NULL) + *(u_int16_t *)frm = htole16(ni->ni_associd); + else + *(u_int16_t *)frm = htole16(0); + frm += 2; + + if (ni != NULL) { + frm = ieee80211_add_rates(frm, &ni->ni_rates); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + } else { + frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); + frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); + } + m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); + break; + + case IEEE80211_FC0_SUBTYPE_DISASSOC: + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "station %s disassociate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg); + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOMEM; + MH_ALIGN(m, 2); + m->m_pkthdr.len = m->m_len = 2; + *mtod(m, u_int16_t *) = htole16(arg); /* reason */ + break; + + default: + IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n", + __func__, type)); + return EINVAL; + } + + ret = ieee80211_mgmt_output(ifp, ni, m, type); + if (ret == 0 && timer) + ic->ic_mgt_timer = timer; + return ret; +} |