Subject: Re: BPF support for writing raw IEEE 802.11 frames
To: None <tech-net@netbsd.org, vraghuna@bbn.com, gdt@ir.bbn.com>
From: Vivek raghunathan <vivek.raghunathan@gmail.com>
List: tech-net
Date: 08/02/2006 12:11:56
David's post has a pointer to Sam Leffler's raw frame injection patch
for FreeBSD (http://people.freebsd.org/~sam/tx80211/kernel.patch). It
seems like he rewires ifp->if_output to ieee80211_output, and writes a
new ieee80211_output function which enqueues raw frames into the
management queue (in ieee80211_raw_xmit). This method is identical to
the first option I had described; I don't like it because it feels
kludgey to enqueue raw (data/ management) frames in the management
queue just to avoid 802.11 encapsulation.

This may have unintended side effects - for example, the Atheros
driver's ath_start function prioritizes service to the management
queue; and may end up starving the normal transmit path when packets
are injected continuously via the bpf write path. I would prefer to
have a separate raw frame queue in the ieee80211com structure, and let
drivers figure out how to service the raw frame queue.

(Correct me if I am wrong here) A separate raw frame queue will also
enable drivers which do not service the management queue at all (e.g.
Aironet) to add raw frame support for the data path cleanly without
having to worry about accidentally transmitting management frames out
when they service the management queue.

Vivek


On 8/1/06, Dheeraj S <dheeraj@ece.gatech.edu> wrote:
> 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.
>
> http://mail-index.netbsd.org/current-users/2006/07/07/0010.html
>
> and the corresponding thread.
>
> --
> dheeraj
>


-- 

---

*************************************
Vivek Raghunathan,
PhD student,
University of Illinois, Urbana-Champaign

Contact Details:
1012 W. Clark St #31,
Urbana IL 61801

ph: 217-766-1868 (cell)
    217-333-7541 (off)