Subject: Re: BPF support for writing raw IEEE 802.11 frames
To: Vivek raghunathan <>
From: Dheeraj S <>
List: tech-net
Date: 08/01/2006 23:59:26
On Tue, Aug 01, 2006 at 02:19:15PM -0400, Vivek raghunathan wrote:

> Hello all,
> (Long email)
> I am currently working at BBN Technologies with Greg Troxel on a project to
> virtualize an IEEE 802.11 interface over a network. The broad
> architecture is as follows: (real/virtual host refers to the host on which
> the real/virtual interface is attached.)
> 1. Frames arriving on the real interface are read by a userspace
> daemon using BPF, encapsulated in UDP packets and passed off to a
> userspace daemon running on the virtual host. This daemon decapsulates
> the UDP packets and injects the extracted 802.11 frames into the
> kernel stack at the virtual host. This injection is done by extending
> the tap(4) interface to support 802.11 in addition to Ethernet.
> 2. When a packet is to be transmitted out the virtual interface, it is
> encapsulated in 802.11 headers and passed to userspace using the
> extended tap(4) interface mentioned above. The userspace daemon at the
> virtual host encapsulates the frames in UDP, and sends them to the
> userspace daemon at the real host, where they are injected into the
> real interface using BPF.
> (There are a lot of additional details here on what it means to
> virtualize an IEEE 802.11 interface over a network - that is the
> subject of another email.)
> As part of this project, we need the ability to inject raw IEEE 802.11
> frames from userspace using BPF. It seems like the current NetBSD
> bpf_write path does not support DLT_IEEE802_11, or
> DLT_IEEE802_11_RADIO. This is because:
> 1. (minor) net/bpf.c: bpf_write calls bpf_movein, which does not
> recognize the dlt DLT_IEEE802_11/DLT_IEEE802_11_RADIO and returns EIO.
> This is easily fixable.
> 2. net/bpf.c: bpf_write passes an mbuf to ifp->if_output. All 802.11
> drivers invoke ether_ifattach via ieee80211_ifattach, and
> ether_ifattach sets ifp->if_output = ether_output. Thus, bpf_write
> ends up passing raw DLT_IEEE802_11 mbufs to ether_output. This causes
> DLT_IEEE80211 raw frame writes on a BPF to break:
> a. ether_output expects raw frames with an attached Ethernet header.
> DLT_IEEE80211 raw frames do not have an Ethernet header.
> b. ether_output enqueues the frame and calls the driver-provided
> foo_start routine, which typically calls ieee80211_encap to do
> IEEE 802.11 encapsulation. If this worked, it would incorrectly put an
> 802.11 header on the raw 802.11 frame. (In practice, it won't work
>          because ieee80211_encap expects its input to be Ethernet
>          encapsulated; it strips off the
>          Ethernet header and adds an IEEE 802.11 header and a LLC
>          header. Here the raw DLT_IEEE802_11 frame does not have an
>          Ethernet header.)
> I am currently working on fixing this, and adding support for
> DLT_IEEE802_11 to the BPF write path. We expect raw DLT_IEEE802_11
> frames to be injected in the same format as they are read: data frames
> consist of a 802.11 header, and LLC header+IP packet in the 802.11
> payload. Management frames consist of an 802.11 header and the
> management frame. There are multiple options on how to implement this,
>           and as I am fairly new to the NetBSD kernel, I was hoping
>           to get some feedback on which way to proceed:
> Option 1: When net80211 code registers DLT_IEEE802_11 with bpf using
> bpfattach, it also pass a callback to be invoked whenever a raw packet
> is written to the bpf device. This callback enqueues the raw 802.11
> frames in the management queue (ieee80211com->mgtq) (whose present
>          semantics are post-802.11 encap).
> Advantage: no changes to driver specific code
> Disadvantage: won't work with drivers that don't poll the management
> queue (e.g., dev/ic/an.c); hack.
> Details:
> a. net/bpf.c: Add a bpf_output function pointer to struct
> bpf_if, and a bpfattach3 function, which is basically bpfattach2 with
> an additional bpf_output parameter that is used to set bpf_output in
> the bpf_if structure.
> b. net/bpf.c: bpf_write - On the bpf_write path,
>     if(bpf_d->bd_bif->bpf_output), call it instead of calling
>     bpf_d->bd_bif->bif_ifp->if_output.
> c. net80211/ieee80211.c:ieee80211_ifattach - use bpfattach3 to install
> DLT_IEEE802_11
> d. (HACK) net80211/ieee80211_output.c: ieee80211_bpf_output - simply
> enqueues the raw frame in the management queue (ic->mgtq), and does
> ifp->if_start
> The hack ensures that we do not need to change each driver's foo_start
> function.
> Option 2: When net80211 code registers DLT_IEEE802_11 with bpf using
> bpfattach, it also pass a callback to be invoked whenever a raw packet
> is written to the bpf device. This callback enqueues the raw 802.11
> frames in a new queue (rawq) in the ieee80211com structure. For each
> driver that wishes to support raw frame injection, we will have to
> (trivially) modify it to poll the raw frame queue in foo_start. This
> option is the same as option 1, except that we add a raw frame queue to
> the ieee80211com structure (ic->rawq) instead of using the management
> queue (ic->mgtq).
> Advantage: cleaner than option 1
> Disadvantage: need to modify each driver's foo_start function.
> Details:
> a, b, c same as Option 1.
> d*. net80211/ieee80211_output.c: ieee80211_bpf_output - enqueues the
> raw frame in the raw frame queue (ic->rawq) and does ifp->if_start
> e. dev/ic/foo.c:foo_start - Drivers that support raw frame BPF writes
> will service the raw frame queue in foo_start similar to how
> management frames are serviced. In other words, no 802.11 encap, and
> foo_start does no checks on the state of the IEEE 802.11 state machine
> for these packets. (Thus, potentially it would be possible to inject a
>          data frame before associating with an AP.)
> Option 3: Change the semantics of BIOCSHDRCMPLT to indicate which
> headers are already present on the written frame. These flags are
> copied into a m_tag and passed around. The current code path is
> modified so that all functions that add headers check the m_tag and
> skip header encapsulation if the corresponding flag is set.
> Advantage: ?
> Disadvantage: ?
> Details:
> a. net/bpf.c:bpf_ioctl - change the semantics of BIOCSHDRCMPLT: the
> value is now a combination of flags representing which headers are
> switched on. Only two are defined for now: HDR_ETHER and
> HDR_IEEE80211.
> b. net/bpf.c:bpf_write - For Ethernet as well as IEEE 802.11,
>     bpf_write constructs and passes the mbuf as before to
>     ifp->if_output = ether_output. It also attaches an m_tag to the
>     mbuf indicating which headers are already present. If the dlt =
>     DLT_IEEE802_11, then m_tag indicates that Ethernet is present,
>     even though it is not actually there.
> c. net/if_ethersubr.c:ether_output - code now checks if the Ethernet
> header is already complete from the m_tag and skips header
> construction.
> d. dev/ic/foo.c:foo_start - driver's foo_start fn checks if the 802.11
> header is already complete from the m_tag and skips IEEE 802.11
> encapsulation.
> -Vivek

You might also want to incorporate David's views regarding the new DLTs for
802.11 injection.

and the corresponding thread.