diff options
author | sam <sam@FreeBSD.org> | 2009-05-20 20:00:40 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2009-05-20 20:00:40 +0000 |
commit | 68f7a1034ab73897585652ceedd3727d57150c12 (patch) | |
tree | c9b0cc01e080db542eb19f65236177aacfa14b5d /sys/net80211/ieee80211_radiotap.c | |
parent | df90a80062bff5ee7300c9b15aaeeaa9c1b49b5e (diff) | |
download | FreeBSD-src-68f7a1034ab73897585652ceedd3727d57150c12.zip FreeBSD-src-68f7a1034ab73897585652ceedd3727d57150c12.tar.gz |
Overhaul monitor mode handling:
o replace DLT_IEEE802_11 support in net80211 with DLT_IEEE802_11_RADIO
and remove explicit bpf support from wireless drivers; drivers now
use ieee80211_radiotap_attach to setup shared data structures that
hold the radiotap header for each packet tx/rx
o remove rx timestamp from the rx path; it was used only by the tdma support
for debugging and was mostly useless due to it being 32-bits and mostly
unavailable
o track DLT_IEEE80211_RADIO bpf attachments and maintain per-vap and
per-com state when there are active taps
o track the number of monitor mode vaps
o use bpf tap and monitor mode vap state to decide when to collect radiotap
state and dispatch frames; drivers no longer explicitly directly check
bpf state or use bpf calls to tap frames
o handle radiotap state updates on channel change in net80211; drivers
should not do this (unless they bypass net80211 which is almost always
a mistake)
o update various drivers to be more consistent/correct in handling radiotap
o update ral to include TSF in radiotap'd frames
o add promisc mode callback to wi
Reviewed by: cbzimmer, rpaulo, thompsa
Diffstat (limited to 'sys/net80211/ieee80211_radiotap.c')
-rw-r--r-- | sys/net80211/ieee80211_radiotap.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/sys/net80211/ieee80211_radiotap.c b/sys/net80211/ieee80211_radiotap.c new file mode 100644 index 0000000..2c4482f --- /dev/null +++ b/sys/net80211/ieee80211_radiotap.c @@ -0,0 +1,325 @@ +/*- + * Copyright (c) 2009 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, 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 DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 radiotap support. + */ +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/endian.h> +#include <sys/kernel.h> + +#include <sys/socket.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_llc.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> + +static int radiotap_offset(struct ieee80211_radiotap_header *, int); + +void +ieee80211_radiotap_attach(struct ieee80211com *ic, + struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, + struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap) +{ +#define B(_v) (1<<(_v)) + int off; + + th->it_len = htole16(roundup2(tlen, sizeof(uint32_t))); + th->it_present = htole32(tx_radiotap); + ic->ic_th = th; + /* calculate offset to channel data */ + off = -1; + if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) + off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL); + else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) + off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL); + if (off == -1) { + if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x", + __func__, tx_radiotap); + /* NB: we handle this case but data will have no chan spec */ + } else + ic->ic_txchan = ((uint8_t *) th) + off; + + rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t))); + rh->it_present = htole32(rx_radiotap); + ic->ic_rh = rh; + /* calculate offset to channel data */ + off = -1; + if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL)) + off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL); + else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL)) + off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL); + if (off == -1) { + if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x", + __func__, rx_radiotap); + /* NB: we handle this case but data will have no chan spec */ + } else + ic->ic_rxchan = ((uint8_t *) rh) + off; +#undef B +} + +void +ieee80211_radiotap_detach(struct ieee80211com *ic) +{ +} + +void +ieee80211_radiotap_vattach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_radiotap_header *th = ic->ic_th; + + KASSERT(th != NULL, ("no radiotap setup")); + + /* radiotap DLT for raw 802.11 frames */ + bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + le16toh(th->it_len), + &vap->iv_rawbpf); +} + +void +ieee80211_radiotap_vdetach(struct ieee80211vap *vap) +{ + /* NB: bpfattach is called by ether_ifdetach and claims all taps */ +} + +static void +set_channel(void *p, const struct ieee80211_channel *c) +{ + struct { + uint16_t freq; + uint16_t flags; + } *rc = p; + + rc->freq = htole16(c->ic_freq); + rc->flags = htole16(c->ic_flags); +} + +static void +set_xchannel(void *p, const struct ieee80211_channel *c) +{ + struct { + uint32_t flags; + uint16_t freq; + uint8_t ieee; + uint8_t maxpow; + } *rc = p; + + rc->flags = htole32(c->ic_flags); + rc->freq = htole16(c->ic_freq); + rc->ieee = c->ic_ieee; + rc->maxpow = c->ic_maxregpower; +} + +/* + * Update radiotap state on channel change. + */ +void +ieee80211_radiotap_chan_change(struct ieee80211com *ic) +{ + if (ic->ic_rxchan != NULL) { + struct ieee80211_radiotap_header *rh = ic->ic_rh; + + if (rh->it_present & (1<<IEEE80211_RADIOTAP_XCHANNEL)) + set_xchannel(ic->ic_rxchan, ic->ic_curchan); + else if (rh->it_present & (1<<IEEE80211_RADIOTAP_CHANNEL)) + set_channel(ic->ic_rxchan, ic->ic_curchan); + } + if (ic->ic_txchan != NULL) { + struct ieee80211_radiotap_header *th = ic->ic_th; + + if (th->it_present & (1<<IEEE80211_RADIOTAP_XCHANNEL)) + set_xchannel(ic->ic_txchan, ic->ic_curchan); + else if (th->it_present & (1<<IEEE80211_RADIOTAP_CHANNEL)) + set_channel(ic->ic_txchan, ic->ic_curchan); + } +} + +static void +dispatch_radiotap(struct ieee80211vap *vap0, struct mbuf *m, + struct ieee80211_radiotap_header *rh) +{ + struct ieee80211com *ic = vap0->iv_ic; + int len = le16toh(rh->it_len); + + if (ieee80211_radiotap_active_vap(vap0)) + bpf_mtap2(vap0->iv_rawbpf, rh, len, m); + if (ic->ic_monvaps) { + struct ieee80211vap *vap; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_opmode == IEEE80211_M_MONITOR && + vap != vap0 && ieee80211_radiotap_active_vap(vap)) + bpf_mtap2(vap->iv_rawbpf, rh, len, m); + } + } +} + +/* + * Dispatch radiotap data for transmitted packet. + */ +void +ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m) +{ + dispatch_radiotap(vap0, m, vap0->iv_ic->ic_th); +} + +/* + * Dispatch radiotap data for received packet. + */ +void +ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m) +{ + dispatch_radiotap(vap0, m, vap0->iv_ic->ic_rh); +} + +/* + * Dispatch radiotap data for a packet received outside the normal + * rx processing path; this is used, for example, to handle frames + * received with errors that would otherwise be dropped. + */ +void +ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m) +{ + struct ieee80211_radiotap_header *rh = ic->ic_rh; + int len = le16toh(rh->it_len); + struct ieee80211vap *vap; + + /* XXX locking? */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (ieee80211_radiotap_active_vap(vap)) + bpf_mtap2(vap->iv_rawbpf, rh, len, m); + } +} + +/* + * Return the offset of the specified item in the radiotap + * header description. If the item is not present or is not + * known -1 is returned. + */ +static int +radiotap_offset(struct ieee80211_radiotap_header *rh, int item) +{ + static const struct { + size_t align, width; + } items[] = { + [IEEE80211_RADIOTAP_TSFT] = { + .align = sizeof(uint64_t), + .width = sizeof(uint64_t), + }, + [IEEE80211_RADIOTAP_FLAGS] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_RATE] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_CHANNEL] = { + .align = sizeof(uint16_t), + .width = 2*sizeof(uint16_t), + }, + [IEEE80211_RADIOTAP_FHSS] = { + .align = sizeof(uint16_t), + .width = sizeof(uint16_t), + }, + [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_LOCK_QUALITY] = { + .align = sizeof(uint16_t), + .width = sizeof(uint16_t), + }, + [IEEE80211_RADIOTAP_TX_ATTENUATION] = { + .align = sizeof(uint16_t), + .width = sizeof(uint16_t), + }, + [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { + .align = sizeof(uint16_t), + .width = sizeof(uint16_t), + }, + [IEEE80211_RADIOTAP_DBM_TX_POWER] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_ANTENNA] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_DB_ANTNOISE] = { + .align = sizeof(uint8_t), + .width = sizeof(uint8_t), + }, + [IEEE80211_RADIOTAP_XCHANNEL] = { + .align = sizeof(uint32_t), + .width = 2*sizeof(uint32_t), + }, + }; + uint32_t present = le32toh(rh->it_present); + int off, i; + + off = sizeof(struct ieee80211_radiotap_header); + for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) { + if ((present & (1<<i)) == 0) + continue; + if (items[i].align == 0) { + /* NB: unidentified element, don't guess */ + printf("%s: unknown item %d\n", __func__, i); + return -1; + } + off = roundup2(off, items[i].align); + if (i == item) { + if (off + items[i].width > le16toh(rh->it_len)) { + /* NB: item does not fit in header data */ + printf("%s: item %d not in header data, " + "off %d width %zu len %d\n", __func__, i, + off, items[i].width, le16toh(rh->it_len)); + return -1; + } + return off; + } + off += items[i].width; + } + return -1; +} |