IEEE 802.11 receive

Hello all,

As part of the ADROIT project at BBN Technologies, we have been working
on getting GNU Radio to be able to receive and decode IEEE 802.11 frames
and inject these frames into the NetBSD kernel networking stack.

As of now, our GNU Radio extensions are successfully receiving and
decoding IEEE 802.11 frames at the base rate (1 Mbps). The 802.11
module is capable of demodulating 1 and 2 Mbps packets when the 1 Mbps
PLCP header is used (sometimes referred to as “long preamble” mode). The
code seems to max out a 1.6 GHz Mobile Pentium. To receive the packets,
the USRP is programmed to produce 8-bit samples (8 bits for I and 8 bits
for Q) at 8 mega samples per second. This means we are looking at about
a 4 MHz (baseband) bandwidth. Although the 802.11 signals are 11
(baseband) MHz wide, there is apparently enough energy in the 4 MHz we
are looking at to receive some 1 Mbps packets, and once in a while
receive a 2 Mbps packet. To save processor resources, the receiver does
not do any phase or frequency synchronization. The receiver is composed
of five blocks - an FIR filter, a time synchronizer block (which I call
slicer), a demodulator block, a de-scrambler block, and a “plcp” block.
The FIR filter attempts to de-spread the barker. The processor spends
more time on this block than any other block. The slicer tries to find
the peaks of the filter output and produces samples at the symbol rate
(1 mega symbols/sec). The demodulator simultaneously demodulates at 1
Mbps (differential BPSK) and 2 Mbps (differential QPSK). There no time
or frequency synchronization. The de-scrambler reverses the 802.11
scrambling operation. And, finally, the “plcp” block runs the receive
state machine. It looks for a preamble, verifies the physical layer
header, and verifies the data frame checksum. If everything checks out,
a small header is prepended to the packet, the frame checksum is
stripped off, and the packet is forwarded up. The RSSI is calculated in
the demodulator block. It averages the energy from 128 successive
samples (output from the slicer), and converts to dB.

To support the injection of IEEE 802.11 frames from user-space into
kernel-space, we have extended the tap(4) interface to support IEEE
802.11 frames in addition to the standard Ethernet mode. It is possible
to use BPF on this tap(4) interface using three different link types:

  1. DLT_EN10MB: Ethernet header
  2. DLT_IEEE802_11: IEEE 802.11 header
  3. DLT_IEEE802_11_RADIO: IEEE 802.11 header + radiotap header for
    meta-data using one of three DLT types

IEEE 802.11 frames received and decoded by GNURadio are written to a
file. A separate user-space program reads these frames from the file and
injects them into the NetBSD kernel using the new tap(4) driver. Receive
meta-data is also pushed into kernel-space in the form of a radio-tap
header in DLT_IEEE802_11_RADIO format.

I am attaching a sample capture of 802.11 received frames using tcpdump
on the extended tap(4) interface with link type set to
DLT_IEEE802_11_RADIO. The first 28 bytes of every packet in the dump
file is the radiotap header, followed by the IEEE 802.11 header, and the
payload. The radiotap header is of the form:

struct tap_rx_radiotap_header {
struct ieee80211_radiotap_header wr_ihdr;
u_int64_t wr_tsf; // currently bogus,= rx timestamp from GNURadio
u_int8_t wr_flags; // 0
u_int8_t wr_rate; // from GnuRadio
u_int16_t wr_chan_freq; // from GnuRadio
u_int16_t wr_chan_flags; // 0
u_int8_t wr_antenna; // 0
u_int8_t wr_antsignal; // from GnuRadio
u_int8_t wr_antnoise; // -100

Note that our GNU Radio IEEE 802.11 receive code currently reports
signal strength in dB with respect to an arbitrary baseline. We haven’t
yet calibrated this to dBm. tcpdump expects signal strength and noise to
be 1 byte unsigned numbers - thus, 234dB reported by tcpdump is really
-20dB from the GNURadio IEEE 802.11 receive code.

-Daniel Sumorok ([email protected])
-Vivek Raghunathan ([email protected], [email protected])
-Greg T. ([email protected])


As I had mentioned in the last email, we have a working implementation
of IEEE 802.11 receive on GNU Radio, and have been able to successfully
decode and inject 802.11 frames into a NetBSD kernel. I forgot to
include a link to our GNU Radio sources in that email. All of our GNU
Radio development is on our public development server at

The IEEE 802.11 receive stuff is in adroitgrdevel/gr-bbn/src/examples/.
A readme file in the directory describes how to run the receiver. There
are currently two python scripts that take the same arguments that can
be used for receiving 802.11 packets. One script just prints out
information about received packets (The other one currently sends
packets to the Linux tap driver, which does not support 802.11 mode. Do
not use this script.)

In order to inject 802.11 frames into the kernel, you’ll need to use
NetBSD and our modified NetBSD tap(4) kernel interface. We are currently
in the process of cleaning up this code to integrate it back into
NetBSD-current. The current “bleeding edge” version of the code is on
our public server at and can be found in