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: releng/10.3/sys/net80211/ieee80211_radiotap.c 237214 2012-06-18 02:08:04Z adrian $"); 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 50237214Sadrianstatic int radiotap_offset(struct ieee80211_radiotap_header *, int, 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{ 57237214Sadrian ieee80211_radiotap_attachv(ic, th, tlen, 0, tx_radiotap, 58237214Sadrian rh, rlen, 0, rx_radiotap); 59237214Sadrian} 60237214Sadrian 61237214Sadrianvoid 62237214Sadrianieee80211_radiotap_attachv(struct ieee80211com *ic, 63237214Sadrian struct ieee80211_radiotap_header *th, 64237214Sadrian int tlen, int n_tx_v, uint32_t tx_radiotap, 65237214Sadrian struct ieee80211_radiotap_header *rh, 66237214Sadrian int rlen, int n_rx_v, uint32_t rx_radiotap) 67237214Sadrian{ 68192468Ssam#define B(_v) (1<<(_v)) 69192468Ssam int off; 70192468Ssam 71192468Ssam th->it_len = htole16(roundup2(tlen, sizeof(uint32_t))); 72192468Ssam th->it_present = htole32(tx_radiotap); 73192468Ssam ic->ic_th = th; 74192468Ssam /* calculate offset to channel data */ 75192468Ssam off = -1; 76192468Ssam if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 77237214Sadrian off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_CHANNEL); 78192468Ssam else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 79237214Sadrian off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_XCHANNEL); 80192468Ssam if (off == -1) { 81232705Sadrian if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x\n", 82192468Ssam __func__, tx_radiotap); 83192468Ssam /* NB: we handle this case but data will have no chan spec */ 84192468Ssam } else 85192468Ssam ic->ic_txchan = ((uint8_t *) th) + off; 86192468Ssam 87192468Ssam rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t))); 88192468Ssam rh->it_present = htole32(rx_radiotap); 89192468Ssam ic->ic_rh = rh; 90192468Ssam /* calculate offset to channel data */ 91192468Ssam off = -1; 92192468Ssam if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) 93237214Sadrian off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_CHANNEL); 94192468Ssam else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) 95237214Sadrian off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_XCHANNEL); 96192468Ssam if (off == -1) { 97232705Sadrian if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x\n", 98192468Ssam __func__, rx_radiotap); 99192468Ssam /* NB: we handle this case but data will have no chan spec */ 100192468Ssam } else 101192468Ssam ic->ic_rxchan = ((uint8_t *) rh) + off; 102192468Ssam#undef B 103192468Ssam} 104192468Ssam 105192468Ssamvoid 106192468Ssamieee80211_radiotap_detach(struct ieee80211com *ic) 107192468Ssam{ 108192468Ssam} 109192468Ssam 110192468Ssamvoid 111192468Ssamieee80211_radiotap_vattach(struct ieee80211vap *vap) 112192468Ssam{ 113192468Ssam struct ieee80211com *ic = vap->iv_ic; 114192468Ssam struct ieee80211_radiotap_header *th = ic->ic_th; 115192468Ssam 116192765Ssam if (th != NULL && ic->ic_rh != NULL) { 117192765Ssam /* radiotap DLT for raw 802.11 frames */ 118192765Ssam bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO, 119192765Ssam sizeof(struct ieee80211_frame) + le16toh(th->it_len), 120192765Ssam &vap->iv_rawbpf); 121192765Ssam } 122192468Ssam} 123192468Ssam 124192468Ssamvoid 125192468Ssamieee80211_radiotap_vdetach(struct ieee80211vap *vap) 126192468Ssam{ 127192468Ssam /* NB: bpfattach is called by ether_ifdetach and claims all taps */ 128192468Ssam} 129192468Ssam 130192468Ssamstatic void 131192468Ssamset_channel(void *p, const struct ieee80211_channel *c) 132192468Ssam{ 133192468Ssam struct { 134192468Ssam uint16_t freq; 135192468Ssam uint16_t flags; 136192468Ssam } *rc = p; 137192468Ssam 138192468Ssam rc->freq = htole16(c->ic_freq); 139192468Ssam rc->flags = htole16(c->ic_flags); 140192468Ssam} 141192468Ssam 142192468Ssamstatic void 143192468Ssamset_xchannel(void *p, const struct ieee80211_channel *c) 144192468Ssam{ 145192468Ssam struct { 146192468Ssam uint32_t flags; 147192468Ssam uint16_t freq; 148192468Ssam uint8_t ieee; 149192468Ssam uint8_t maxpow; 150192468Ssam } *rc = p; 151192468Ssam 152192468Ssam rc->flags = htole32(c->ic_flags); 153192468Ssam rc->freq = htole16(c->ic_freq); 154192468Ssam rc->ieee = c->ic_ieee; 155192468Ssam rc->maxpow = c->ic_maxregpower; 156192468Ssam} 157192468Ssam 158192468Ssam/* 159192468Ssam * Update radiotap state on channel change. 160192468Ssam */ 161192468Ssamvoid 162192468Ssamieee80211_radiotap_chan_change(struct ieee80211com *ic) 163192468Ssam{ 164192468Ssam if (ic->ic_rxchan != NULL) { 165192468Ssam struct ieee80211_radiotap_header *rh = ic->ic_rh; 166192468Ssam 167193760Ssam if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 168192468Ssam set_xchannel(ic->ic_rxchan, ic->ic_curchan); 169193760Ssam else if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 170192468Ssam set_channel(ic->ic_rxchan, ic->ic_curchan); 171192468Ssam } 172192468Ssam if (ic->ic_txchan != NULL) { 173192468Ssam struct ieee80211_radiotap_header *th = ic->ic_th; 174192468Ssam 175193760Ssam if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL)) 176192468Ssam set_xchannel(ic->ic_txchan, ic->ic_curchan); 177193760Ssam else if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL)) 178192468Ssam set_channel(ic->ic_txchan, ic->ic_curchan); 179192468Ssam } 180192468Ssam} 181192468Ssam 182192468Ssam/* 183193292Ssam * Distribute radiotap data (+packet) to all monitor mode 184193292Ssam * vaps with an active tap other than vap0. 185193292Ssam */ 186193292Ssamstatic void 187193292Ssamspam_vaps(struct ieee80211vap *vap0, struct mbuf *m, 188193292Ssam struct ieee80211_radiotap_header *rh, int len) 189193292Ssam{ 190193292Ssam struct ieee80211com *ic = vap0->iv_ic; 191193292Ssam struct ieee80211vap *vap; 192193292Ssam 193193292Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 194193292Ssam if (vap != vap0 && 195193292Ssam vap->iv_opmode == IEEE80211_M_MONITOR && 196193292Ssam (vap->iv_flags_ext & IEEE80211_FEXT_BPF) && 197193292Ssam vap->iv_state != IEEE80211_S_INIT) 198193292Ssam bpf_mtap2(vap->iv_rawbpf, rh, len, m); 199193292Ssam } 200193292Ssam} 201193292Ssam 202193292Ssam/* 203192468Ssam * Dispatch radiotap data for transmitted packet. 204192468Ssam */ 205192468Ssamvoid 206192468Ssamieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m) 207192468Ssam{ 208193292Ssam struct ieee80211com *ic = vap0->iv_ic; 209193292Ssam struct ieee80211_radiotap_header *th = ic->ic_th; 210193292Ssam int len; 211193292Ssam 212193292Ssam KASSERT(th != NULL, ("no tx radiotap header")); 213193292Ssam len = le16toh(th->it_len); 214193292Ssam 215193292Ssam if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 216193292Ssam bpf_mtap2(vap0->iv_rawbpf, th, len, m); 217193292Ssam /* 218193292Ssam * Spam monitor mode vaps. 219193292Ssam */ 220193292Ssam if (ic->ic_montaps != 0) 221193292Ssam spam_vaps(vap0, m, th, len); 222192468Ssam} 223192468Ssam 224192468Ssam/* 225192468Ssam * Dispatch radiotap data for received packet. 226192468Ssam */ 227192468Ssamvoid 228192468Ssamieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m) 229192468Ssam{ 230193292Ssam struct ieee80211com *ic = vap0->iv_ic; 231193292Ssam struct ieee80211_radiotap_header *rh = ic->ic_rh; 232193292Ssam int len; 233193292Ssam 234193292Ssam KASSERT(rh != NULL, ("no rx radiotap header")); 235193292Ssam len = le16toh(rh->it_len); 236193292Ssam 237193292Ssam if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF) 238193292Ssam bpf_mtap2(vap0->iv_rawbpf, rh, len, m); 239193292Ssam /* 240193292Ssam * Spam monitor mode vaps with unicast frames. Multicast 241193292Ssam * frames are handled by passing through ieee80211_input_all 242193292Ssam * which distributes copies to the monitor mode vaps. 243193292Ssam */ 244193292Ssam if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0) 245193292Ssam spam_vaps(vap0, m, rh, len); 246192468Ssam} 247192468Ssam 248192468Ssam/* 249192468Ssam * Dispatch radiotap data for a packet received outside the normal 250192468Ssam * rx processing path; this is used, for example, to handle frames 251192468Ssam * received with errors that would otherwise be dropped. 252192468Ssam */ 253192468Ssamvoid 254192468Ssamieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m) 255192468Ssam{ 256192468Ssam struct ieee80211_radiotap_header *rh = ic->ic_rh; 257192468Ssam int len = le16toh(rh->it_len); 258192468Ssam struct ieee80211vap *vap; 259192468Ssam 260192468Ssam /* XXX locking? */ 261192468Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 262193292Ssam if (ieee80211_radiotap_active_vap(vap) && 263193292Ssam vap->iv_state != IEEE80211_S_INIT) 264192468Ssam bpf_mtap2(vap->iv_rawbpf, rh, len, m); 265192468Ssam } 266192468Ssam} 267192468Ssam 268192468Ssam/* 269192468Ssam * Return the offset of the specified item in the radiotap 270192468Ssam * header description. If the item is not present or is not 271192468Ssam * known -1 is returned. 272192468Ssam */ 273192468Ssamstatic int 274237214Sadrianradiotap_offset(struct ieee80211_radiotap_header *rh, 275237214Sadrian int n_vendor_attributes, int item) 276192468Ssam{ 277192468Ssam static const struct { 278192468Ssam size_t align, width; 279192468Ssam } items[] = { 280192468Ssam [IEEE80211_RADIOTAP_TSFT] = { 281192468Ssam .align = sizeof(uint64_t), 282192468Ssam .width = sizeof(uint64_t), 283192468Ssam }, 284192468Ssam [IEEE80211_RADIOTAP_FLAGS] = { 285192468Ssam .align = sizeof(uint8_t), 286192468Ssam .width = sizeof(uint8_t), 287192468Ssam }, 288192468Ssam [IEEE80211_RADIOTAP_RATE] = { 289192468Ssam .align = sizeof(uint8_t), 290192468Ssam .width = sizeof(uint8_t), 291192468Ssam }, 292192468Ssam [IEEE80211_RADIOTAP_CHANNEL] = { 293192468Ssam .align = sizeof(uint16_t), 294192468Ssam .width = 2*sizeof(uint16_t), 295192468Ssam }, 296192468Ssam [IEEE80211_RADIOTAP_FHSS] = { 297192468Ssam .align = sizeof(uint16_t), 298192468Ssam .width = sizeof(uint16_t), 299192468Ssam }, 300192468Ssam [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { 301192468Ssam .align = sizeof(uint8_t), 302192468Ssam .width = sizeof(uint8_t), 303192468Ssam }, 304192468Ssam [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { 305192468Ssam .align = sizeof(uint8_t), 306192468Ssam .width = sizeof(uint8_t), 307192468Ssam }, 308192468Ssam [IEEE80211_RADIOTAP_LOCK_QUALITY] = { 309192468Ssam .align = sizeof(uint16_t), 310192468Ssam .width = sizeof(uint16_t), 311192468Ssam }, 312192468Ssam [IEEE80211_RADIOTAP_TX_ATTENUATION] = { 313192468Ssam .align = sizeof(uint16_t), 314192468Ssam .width = sizeof(uint16_t), 315192468Ssam }, 316192468Ssam [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { 317192468Ssam .align = sizeof(uint16_t), 318192468Ssam .width = sizeof(uint16_t), 319192468Ssam }, 320192468Ssam [IEEE80211_RADIOTAP_DBM_TX_POWER] = { 321192468Ssam .align = sizeof(uint8_t), 322192468Ssam .width = sizeof(uint8_t), 323192468Ssam }, 324192468Ssam [IEEE80211_RADIOTAP_ANTENNA] = { 325192468Ssam .align = sizeof(uint8_t), 326192468Ssam .width = sizeof(uint8_t), 327192468Ssam }, 328192468Ssam [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { 329192468Ssam .align = sizeof(uint8_t), 330192468Ssam .width = sizeof(uint8_t), 331192468Ssam }, 332192468Ssam [IEEE80211_RADIOTAP_DB_ANTNOISE] = { 333192468Ssam .align = sizeof(uint8_t), 334192468Ssam .width = sizeof(uint8_t), 335192468Ssam }, 336192468Ssam [IEEE80211_RADIOTAP_XCHANNEL] = { 337192468Ssam .align = sizeof(uint32_t), 338192468Ssam .width = 2*sizeof(uint32_t), 339192468Ssam }, 340229952Sadrian [IEEE80211_RADIOTAP_MCS] = { 341229952Sadrian .align = sizeof(uint8_t), 342229952Sadrian .width = 3*sizeof(uint8_t), 343229952Sadrian }, 344192468Ssam }; 345192468Ssam uint32_t present = le32toh(rh->it_present); 346192468Ssam int off, i; 347192468Ssam 348192468Ssam off = sizeof(struct ieee80211_radiotap_header); 349237214Sadrian off += n_vendor_attributes * (sizeof(uint32_t)); 350237214Sadrian 351192468Ssam for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) { 352192468Ssam if ((present & (1<<i)) == 0) 353192468Ssam continue; 354192468Ssam if (items[i].align == 0) { 355192468Ssam /* NB: unidentified element, don't guess */ 356192468Ssam printf("%s: unknown item %d\n", __func__, i); 357192468Ssam return -1; 358192468Ssam } 359192468Ssam off = roundup2(off, items[i].align); 360192468Ssam if (i == item) { 361192468Ssam if (off + items[i].width > le16toh(rh->it_len)) { 362192468Ssam /* NB: item does not fit in header data */ 363192468Ssam printf("%s: item %d not in header data, " 364192468Ssam "off %d width %zu len %d\n", __func__, i, 365192468Ssam off, items[i].width, le16toh(rh->it_len)); 366192468Ssam return -1; 367192468Ssam } 368192468Ssam return off; 369192468Ssam } 370192468Ssam off += items[i].width; 371192468Ssam } 372192468Ssam return -1; 373192468Ssam} 374