1192468Ssam/*- 2192468Ssam * Copyright (c) 2009 Sam Leffler, Errno Consulting 3192468Ssam * All rights reserved. 4192468Ssam * 5192468Ssam * Redistribution and use in source and binary forms, with or without 6192468Ssam * modification, are permitted provided that the following conditions 7192468Ssam * are met: 8192468Ssam * 1. Redistributions of source code must retain the above copyright 9192468Ssam * notice, this list of conditions and the following disclaimer. 10192468Ssam * 2. Redistributions in binary form must reproduce the above copyright 11192468Ssam * notice, this list of conditions and the following disclaimer in the 12192468Ssam * documentation and/or other materials provided with the distribution. 13192468Ssam * 14192468Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15192468Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16192468Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17192468Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18192468Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19192468Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20192468Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21192468Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22192468Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23192468Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24192468Ssam */ 25192468Ssam 26192468Ssam#include <sys/cdefs.h> 27192468Ssam__FBSDID("$FreeBSD$"); 28192468Ssam 29192468Ssam/* 30192468Ssam * IEEE 802.11 radiotap support. 31192468Ssam */ 32192468Ssam#include "opt_wlan.h" 33192468Ssam 34192468Ssam#include <sys/param.h> 35192468Ssam#include <sys/systm.h> 36192468Ssam#include <sys/mbuf.h> 37192468Ssam#include <sys/malloc.h> 38192468Ssam#include <sys/endian.h> 39192468Ssam#include <sys/kernel.h> 40192468Ssam 41192468Ssam#include <sys/socket.h> 42192468Ssam 43192468Ssam#include <net/bpf.h> 44192468Ssam#include <net/if.h> 45192468Ssam#include <net/if_llc.h> 46192468Ssam#include <net/if_media.h> 47192468Ssam 48192468Ssam#include <net80211/ieee80211_var.h> 49192468Ssam 50192468Ssamstatic int radiotap_offset(struct ieee80211_radiotap_header *, int); 51192468Ssam 52192468Ssamvoid 53192468Ssamieee80211_radiotap_attach(struct ieee80211com *ic, 54192468Ssam struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, 55192468Ssam struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap) 56192468Ssam{ 57192468Ssam#define B(_v) (1<<(_v)) 58192468Ssam int off; 59192468Ssam 60192468Ssam th->it_len = htole16(roundup2(tlen, sizeof(uint32_t))); 61192468Ssam th->it_present = htole32(tx_radiotap); 62192468Ssam ic->ic_th = th; 63192468Ssam /* calculate offset to channel data */ 64192468Ssam off = -1; 65192468Ssam if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 66192468Ssam off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL); 67192468Ssam else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 68192468Ssam off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL); 69192468Ssam if (off == -1) { 70192468Ssam if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x", 71192468Ssam __func__, tx_radiotap); 72192468Ssam /* NB: we handle this case but data will have no chan spec */ 73192468Ssam } else 74192468Ssam ic->ic_txchan = ((uint8_t *) th) + off; 75192468Ssam 76192468Ssam rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t))); 77192468Ssam rh->it_present = htole32(rx_radiotap); 78192468Ssam ic->ic_rh = rh; 79192468Ssam /* calculate offset to channel data */ 80192468Ssam off = -1; 81192468Ssam if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 82192468Ssam off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL); 83192468Ssam else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 84192468Ssam off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL); 85192468Ssam if (off == -1) { 86192468Ssam if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x", 87192468Ssam __func__, rx_radiotap); 88192468Ssam /* NB: we handle this case but data will have no chan spec */ 89192468Ssam } else 90192468Ssam ic->ic_rxchan = ((uint8_t *) rh) + off; 91192468Ssam#undef B 92192468Ssam} 93192468Ssam 94192468Ssamvoid 95192468Ssamieee80211_radiotap_detach(struct ieee80211com *ic) 96192468Ssam{ 97192468Ssam} 98192468Ssam 99192468Ssamvoid 100192468Ssamieee80211_radiotap_vattach(struct ieee80211vap *vap) 101192468Ssam{ 102192468Ssam struct ieee80211com *ic = vap->iv_ic; 103192468Ssam struct ieee80211_radiotap_header *th = ic->ic_th; 104192468Ssam 105192765Ssam if (th != NULL && ic->ic_rh != NULL) { 106192765Ssam /* radiotap DLT for raw 802.11 frames */ 107192765Ssam bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO, 108192765Ssam sizeof(struct ieee80211_frame) + le16toh(th->it_len), 109192765Ssam &vap->iv_rawbpf); 110192765Ssam } 111192468Ssam} 112192468Ssam 113192468Ssamvoid 114192468Ssamieee80211_radiotap_vdetach(struct ieee80211vap *vap) 115192468Ssam{ 116192468Ssam /* NB: bpfattach is called by ether_ifdetach and claims all taps */ 117192468Ssam} 118192468Ssam 119192468Ssamstatic void 120192468Ssamset_channel(void *p, const struct ieee80211_channel *c) 121192468Ssam{ 122192468Ssam struct { 123192468Ssam uint16_t freq; 124192468Ssam uint16_t flags; 125192468Ssam } *rc = p; 126192468Ssam 127192468Ssam rc->freq = htole16(c->ic_freq); 128192468Ssam rc->flags = htole16(c->ic_flags); 129192468Ssam} 130192468Ssam 131192468Ssamstatic void 132192468Ssamset_xchannel(void *p, const struct ieee80211_channel *c) 133192468Ssam{ 134192468Ssam struct { 135192468Ssam uint32_t flags; 136192468Ssam uint16_t freq; 137192468Ssam uint8_t ieee; 138192468Ssam uint8_t maxpow; 139192468Ssam } *rc = p; 140192468Ssam 141192468Ssam rc->flags = htole32(c->ic_flags); 142192468Ssam rc->freq = htole16(c->ic_freq); 143192468Ssam rc->ieee = c->ic_ieee; 144192468Ssam rc->maxpow = c->ic_maxregpower; 145192468Ssam} 146192468Ssam 147192468Ssam/* 148192468Ssam * Update radiotap state on channel change. 149192468Ssam */ 150192468Ssamvoid 151192468Ssamieee80211_radiotap_chan_change(struct ieee80211com *ic) 152192468Ssam{ 153192468Ssam if (ic->ic_rxchan != NULL) { 154192468Ssam struct ieee80211_radiotap_header *rh = ic->ic_rh; 155192468Ssam 156193760Ssam if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 157192468Ssam set_xchannel(ic->ic_rxchan, ic->ic_curchan); 158193760Ssam else if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 159192468Ssam set_channel(ic->ic_rxchan, ic->ic_curchan); 160192468Ssam } 161192468Ssam if (ic->ic_txchan != NULL) { 162192468Ssam struct ieee80211_radiotap_header *th = ic->ic_th; 163192468Ssam 164193760Ssam if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 165192468Ssam set_xchannel(ic->ic_txchan, ic->ic_curchan); 166193760Ssam else if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 167192468Ssam set_channel(ic->ic_txchan, ic->ic_curchan); 168192468Ssam } 169192468Ssam} 170192468Ssam 171192468Ssam/* 172193292Ssam * Distribute radiotap data (+packet) to all monitor mode 173193292Ssam * vaps with an active tap other than vap0. 174193292Ssam */ 175193292Ssamstatic void 176193292Ssamspam_vaps(struct ieee80211vap *vap0, struct mbuf *m, 177193292Ssam struct ieee80211_radiotap_header *rh, int len) 178193292Ssam{ 179193292Ssam struct ieee80211com *ic = vap0->iv_ic; 180193292Ssam struct ieee80211vap *vap; 181193292Ssam 182193292Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 183193292Ssam if (vap != vap0 && 184193292Ssam vap->iv_opmode == IEEE80211_M_MONITOR && 185193292Ssam (vap->iv_flags_ext & IEEE80211_FEXT_BPF) && 186193292Ssam vap->iv_state != IEEE80211_S_INIT) 187193292Ssam bpf_mtap2(vap->iv_rawbpf, rh, len, m); 188193292Ssam } 189193292Ssam} 190193292Ssam 191193292Ssam/* 192192468Ssam * Dispatch radiotap data for transmitted packet. 193192468Ssam */ 194192468Ssamvoid 195192468Ssamieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m) 196192468Ssam{ 197193292Ssam struct ieee80211com *ic = vap0->iv_ic; 198193292Ssam struct ieee80211_radiotap_header *th = ic->ic_th; 199193292Ssam int len; 200193292Ssam 201193292Ssam KASSERT(th != NULL, ("no tx radiotap header")); 202193292Ssam len = le16toh(th->it_len); 203193292Ssam 204193292Ssam if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 205193292Ssam bpf_mtap2(vap0->iv_rawbpf, th, len, m); 206193292Ssam /* 207193292Ssam * Spam monitor mode vaps. 208193292Ssam */ 209193292Ssam if (ic->ic_montaps != 0) 210193292Ssam spam_vaps(vap0, m, th, len); 211192468Ssam} 212192468Ssam 213192468Ssam/* 214192468Ssam * Dispatch radiotap data for received packet. 215192468Ssam */ 216192468Ssamvoid 217192468Ssamieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m) 218192468Ssam{ 219193292Ssam struct ieee80211com *ic = vap0->iv_ic; 220193292Ssam struct ieee80211_radiotap_header *rh = ic->ic_rh; 221193292Ssam int len; 222193292Ssam 223193292Ssam KASSERT(rh != NULL, ("no rx radiotap header")); 224193292Ssam len = le16toh(rh->it_len); 225193292Ssam 226193292Ssam if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 227193292Ssam bpf_mtap2(vap0->iv_rawbpf, rh, len, m); 228193292Ssam /* 229193292Ssam * Spam monitor mode vaps with unicast frames. Multicast 230193292Ssam * frames are handled by passing through ieee80211_input_all 231193292Ssam * which distributes copies to the monitor mode vaps. 232193292Ssam */ 233193292Ssam if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0) 234193292Ssam spam_vaps(vap0, m, rh, len); 235192468Ssam} 236192468Ssam 237192468Ssam/* 238192468Ssam * Dispatch radiotap data for a packet received outside the normal 239192468Ssam * rx processing path; this is used, for example, to handle frames 240192468Ssam * received with errors that would otherwise be dropped. 241192468Ssam */ 242192468Ssamvoid 243192468Ssamieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m) 244192468Ssam{ 245192468Ssam struct ieee80211_radiotap_header *rh = ic->ic_rh; 246192468Ssam int len = le16toh(rh->it_len); 247192468Ssam struct ieee80211vap *vap; 248192468Ssam 249192468Ssam /* XXX locking? */ 250192468Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 251193292Ssam if (ieee80211_radiotap_active_vap(vap) && 252193292Ssam vap->iv_state != IEEE80211_S_INIT) 253192468Ssam bpf_mtap2(vap->iv_rawbpf, rh, len, m); 254192468Ssam } 255192468Ssam} 256192468Ssam 257192468Ssam/* 258192468Ssam * Return the offset of the specified item in the radiotap 259192468Ssam * header description. If the item is not present or is not 260192468Ssam * known -1 is returned. 261192468Ssam */ 262192468Ssamstatic int 263192468Ssamradiotap_offset(struct ieee80211_radiotap_header *rh, int item) 264192468Ssam{ 265192468Ssam static const struct { 266192468Ssam size_t align, width; 267192468Ssam } items[] = { 268192468Ssam [IEEE80211_RADIOTAP_TSFT] = { 269192468Ssam .align = sizeof(uint64_t), 270192468Ssam .width = sizeof(uint64_t), 271192468Ssam }, 272192468Ssam [IEEE80211_RADIOTAP_FLAGS] = { 273192468Ssam .align = sizeof(uint8_t), 274192468Ssam .width = sizeof(uint8_t), 275192468Ssam }, 276192468Ssam [IEEE80211_RADIOTAP_RATE] = { 277192468Ssam .align = sizeof(uint8_t), 278192468Ssam .width = sizeof(uint8_t), 279192468Ssam }, 280192468Ssam [IEEE80211_RADIOTAP_CHANNEL] = { 281192468Ssam .align = sizeof(uint16_t), 282192468Ssam .width = 2*sizeof(uint16_t), 283192468Ssam }, 284192468Ssam [IEEE80211_RADIOTAP_FHSS] = { 285192468Ssam .align = sizeof(uint16_t), 286192468Ssam .width = sizeof(uint16_t), 287192468Ssam }, 288192468Ssam [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { 289192468Ssam .align = sizeof(uint8_t), 290192468Ssam .width = sizeof(uint8_t), 291192468Ssam }, 292192468Ssam [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { 293192468Ssam .align = sizeof(uint8_t), 294192468Ssam .width = sizeof(uint8_t), 295192468Ssam }, 296192468Ssam [IEEE80211_RADIOTAP_LOCK_QUALITY] = { 297192468Ssam .align = sizeof(uint16_t), 298192468Ssam .width = sizeof(uint16_t), 299192468Ssam }, 300192468Ssam [IEEE80211_RADIOTAP_TX_ATTENUATION] = { 301192468Ssam .align = sizeof(uint16_t), 302192468Ssam .width = sizeof(uint16_t), 303192468Ssam }, 304192468Ssam [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { 305192468Ssam .align = sizeof(uint16_t), 306192468Ssam .width = sizeof(uint16_t), 307192468Ssam }, 308192468Ssam [IEEE80211_RADIOTAP_DBM_TX_POWER] = { 309192468Ssam .align = sizeof(uint8_t), 310192468Ssam .width = sizeof(uint8_t), 311192468Ssam }, 312192468Ssam [IEEE80211_RADIOTAP_ANTENNA] = { 313192468Ssam .align = sizeof(uint8_t), 314192468Ssam .width = sizeof(uint8_t), 315192468Ssam }, 316192468Ssam [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { 317192468Ssam .align = sizeof(uint8_t), 318192468Ssam .width = sizeof(uint8_t), 319192468Ssam }, 320192468Ssam [IEEE80211_RADIOTAP_DB_ANTNOISE] = { 321192468Ssam .align = sizeof(uint8_t), 322192468Ssam .width = sizeof(uint8_t), 323192468Ssam }, 324192468Ssam [IEEE80211_RADIOTAP_XCHANNEL] = { 325192468Ssam .align = sizeof(uint32_t), 326192468Ssam .width = 2*sizeof(uint32_t), 327192468Ssam }, 328192468Ssam }; 329192468Ssam uint32_t present = le32toh(rh->it_present); 330192468Ssam int off, i; 331192468Ssam 332192468Ssam off = sizeof(struct ieee80211_radiotap_header); 333192468Ssam for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) { 334192468Ssam if ((present & (1<<i)) == 0) 335192468Ssam continue; 336192468Ssam if (items[i].align == 0) { 337192468Ssam /* NB: unidentified element, don't guess */ 338192468Ssam printf("%s: unknown item %d\n", __func__, i); 339192468Ssam return -1; 340192468Ssam } 341192468Ssam off = roundup2(off, items[i].align); 342192468Ssam if (i == item) { 343192468Ssam if (off + items[i].width > le16toh(rh->it_len)) { 344192468Ssam /* NB: item does not fit in header data */ 345192468Ssam printf("%s: item %d not in header data, " 346192468Ssam "off %d width %zu len %d\n", __func__, i, 347192468Ssam off, items[i].width, le16toh(rh->it_len)); 348192468Ssam return -1; 349192468Ssam } 350192468Ssam return off; 351192468Ssam } 352192468Ssam off += items[i].width; 353192468Ssam } 354192468Ssam return -1; 355192468Ssam} 356