net80211: add initial README.md and PROTOCOL.md

* Add top level README.md, linking to in progress and todo items
* Add an 802.11 protocol overview, with todo items - this is designed
  to provide a basic introduction to 802.11 for people wishing to work
  on net80211 and drivers.
* DEBUG.md - cover the debug API
* DATAPATH_TRANSMIT.md - transmit datapath
* DATAPATH_RECEIVE.md - receive datapath
* PROTOCOL.md - a high level (for values of "high") overview of
  the 802.11 protocol and where it intersects with net80211

Differential Revision: https://reviews.freebsd.org/D56760
This commit is contained in:
Adrian Chadd committed 2026-05-24 18:32:33 -07:00
1 parent 1ccaef9867
commit 490c53e935
6 files changed
+1355 -1

No files matched your search

+160
View File
@@ -0,0 +1,160 @@
# net80211 Datapath - Receive
## Overview
This document provides an overview for receive data paths in
net80211, between the interface to the operating system, through net80211 and
into the driver.
The details about underlying implementations (eg how A-MPDU RX aggregation
is handled) will be covered in dedicated documents.
## Concurrency Notes
The transmit path(s), receive path and control / ioctl paths all run
in parallel and can be scheduled on multiple concurrently running
kernel threads. It's important to keep this in mind.
## Receive Path
### Concurrency
There must only be one packet receive path into net80211. The net80211 stack
has not yet been fully validated to ensure that state changes all occur under
sufficient locking.
### Data Path
The receive path is split into three broad categories:
* The normal 802.11/802.3 packet receive path from drivers;
* The input path for reinjected frames (eg WDS, 802.11s, BPF);
* Various side channels for offloaded non-data path (eg explicitly
scan results, management frames, etc.)
#### Data Path - Initial Input
The driver receive path begins in ieee80211_input.c . The four
entry points are:
* ieee80211_input() / ieee80211_input_mimo() and
* ieee80211_input_all() / ieee80211_input_mimo_all().
The first two are called when the destination MAC address is a known
(struct ieee80211_node) node. These are passed up to the
VAP via a call to ni->ni_vap->iv_input().
The second two are called when the destination MAC address is NOT
a known node. In this instance, the frames are treated as broadcast
and routed to each VAP BSS node via a call to ieee80211_input_mimo().
Each VAP vap->iv_input() method handles the behavioural specific
needs of the interface.
#### Data Path - VAP type / behaviour
Each VAP type will do roughly the same thing - for example see
sta_input() in ieee80211_sta.c .
* Check the frame size and protocol ID;
* Check if the frame has been decrypted in hardware;
* Grab A-MPDU session frames and put them in the reorder queue;
* Handle control frames sent to the node, or general scan frames;
* Get the frame QoS information / TID information if present;
* If appropriate, check the 802.11 receive sequence number;
* Break the handling up into data, management and control;
* Reinject into a radiotap/BPF session via a call to
ieee80211_radiotap_rx().
The data paths will typically do the following:
* Do decryption if needed;
* Do 802.11 decap if needed;
* Enforce security requirements if needed;
* Eventually deliver the frame up to the higher level network
stack via a call to ieee80211_deliver_data() which will
strip away any last bits of 802.11 / net80211,
call ieee80211_vap_deliver_data(), which will call the
network stack input interface.
The control and management paths will call vap->iv_recv_mgmt()
and vap->iv_recv_ctl() which implement the per VAP type behaviours.
These will include participating in driving the scan engine,
the per-node state machines and the VAP state machine.
#### Reinjected Path
#### Side Channels
Drivers may need a specific side channel for management/control
frames, MAC layer events (eg A-MPDU aggregation session state);
some power state communication, scan information and other
things that would normally show up as 802.11 frames.
These will be covered in more detail in other documents.
### Receive Status and Parameters
Received 802.11 / 802.3 frames can come with a variety of information
that isn't strictly the data payload. These include receive timestamps
(at beginning or end of frame), receive noise floor / signal strength,
channel / frequency, channel width, received rate, aggregation frame
boundaries, decryption state, etc.
The original paths - ieee80211_input() and ieee80211_input_all() -
took a noise floor and rssi parameter. Later drivers provide
information about all of the above by attaching a (struct ieee80211_rx_stats)
to the receive mbuf via a call to ieee80211_add_rx_params() bafore
calling ieee80211_input_mimo() and ieee80211_input_mimo_all() .
Existing drivers should be migrated to the mimo versions of these
APIs and the existing API should eventually be deprecated and
replace the mimo versions.
All new drivers must use the ieee80211_input_mimo() and
ieee80211_input_mimo_all() API calls.
### Driver Receive Path Requirements
The driver receive path has a few top level requirements:
* Driver / stack locks must not be held during receive. This means that
drivers should dequeue their frames first into a local list, release
whatever locks are needed and then pass the frames up to net80211.
* Drivers are responsible for doing the node lookup before
calling ieee80211_input() / ieee80211_input_mimo() or
calling ieee80211_input_all() / ieee80211_input_mimo_all().
* Drivers are also responsible for creating and attaching the
ieee80211_rx_stats information via a call to ieee80211_add_rx_params().
* Drivers are responsible for tagging a frame as a potential
A_MPDU by tagging the received mbuf with the M_AMPDU flag.
They should do this by just tagging all mbufs to a node
with ni->ni_flags & IEEE80211_NODE_HT set w/ the M_AMPDU flag.
This is a holdover from the 802.11n code which enforces that
only potential AMPDU frames can be added to an A-MPDU receive
aggregation session and may be relaxed / removed in the future.
### Driver Receive Path Methods
Drivers can hook into the receive path processing in a variety of ways.
There are a number of vap methods that a driver can hook into
processing. The details will be covered in the driver document.
These include:
* vap->iv_input - the driver can replace the iv_input method
with its own method to first handle frames before they are passed
to the VAP type receive path.
* vap->iv_recv_mgmt - the driver can hook here to handle
management frames before the VAP type management receive path.
* vap->iv_recv_ctl - the driver can hook here to handle
control frames before the VAP type control receive path.
* vap->iv_bmiss - the driver can hook here to be informed of
beacon miss frames.
These may be called at any time and overlapping with others (eg
the beacon miss event - which may be triggered by a timer -
can be called in parallel with the various receive path methods.)
+390
View File
@@ -0,0 +1,390 @@
# net80211 Datapath - Transmit
## Overview
This document provides an overview for the transmit data path in
net80211, between the interface to the operating system, through net80211 and
into the driver.
The details about underlying implementations (eg how A-MPDU RX aggregation
is handled) will be covered in dedicated documents.
## Concurrency Notes
The transmit path(s), receive path and control / ioctl paths all run
in parallel and can be scheduled on multiple concurrently running
kernel threads. It's important to keep this in mind.
## Transmit Path
There are two paths from the operating system layer into the net80211 transmit
path - the normal data path and the BPF / radiotap raw frame path.
It is important to note that both paths have no serialisation between
them, and multiple sending paths in the OS can and will queue frames
simultaneously across multiple concurrently executing threads/CPUs.
Please keep this in mind when reading the transmit handling and
how it interacts with 802.11 sequence numbering and encryption IV.
### Data Path - net80211
This is configured at the ifnet setup in ieee80211_vap_setup() -
the output path is ieee80211_vap_transmit(). This input path
takes 802.3 ethernet frames with no attached metadata (such as
rate control, transmit power, etc) - it is left up to the stack.
This hands the packet off to ieee80211_start_pkt() which will
perform the initial 802.11 destination lookup, query the node
state (eg whether it's in power save) and the VAP state (eg
is the vap itself in power state, or in a non-RUN state)
and drop or queue the frame appropriately.
It is then handed over to ieee80211_vap_pkt_send_dest() with
a destination ieee80211_node reference.
ieee80211_vap_pkt_send_dest() performs the bulk of the
net80211 transmit handling. Packets will be queued here if the
destination node is in a power saving mode.
This includes:
* Firstly - checking if the packet needs to be queued for
power saving operation and will pass it via ieee80211_pwrsave()
if needed;
* QoS classification via a call to ieee80211_classify();
* BPF TX tap via a call to BPF_MTAP();
* handling 802.11 encapsulation via ieee80211_encap() if required;
* A-MPDU TX decisions, AMSDU and Atheros Fast-Frames decisions.
At this point the packet has been 802.11 encapsulated if required,
marked as needing encryption if required, and has been optionally
fragmented into a list of 802.11 fragments.
Finally, the packet / fragment packet chain is sent up to the driver via a call
to ieee80211_parent_xmitpkt(). The driver is expected to queue the
packet / fragment list or discard the packet / fragment list. The specific
format of the mbuf chain and how ieee80211_node references are kept
is documented in ieee80211_parent_xmitpkt().
#### Notes on transmit path serialisation
Note that by default the IEEE80211_TX_LOCK() is held across the call to
ieee80211_encap() and ieee80211_parent_xmitpkt(). Drivers can register
that they properly handle 802.11 sequence number offloading via
IEEE80211_FEXT_SEQNO_OFFLOAD. The lock is to ensure that packets
queued to the driver layer are added to the driver transmit queue
in the same order that they are 802.11 encapsulated - which sets the
802.11 sequence number. Drivers which set IEEE80211_FEXT_SEQNO_OFFLOAD
indicate that they will assign the sequence number themselves - likely
at the same time that the transmit encryption IV number is assigned,
or simply offloaded in firmware - and thus this lock is not
required.
### Data path - Driver
The call ieee80211_parent_xmit() will call the driver ic->ic_transmit()
method. At this point the driver can choose to queue / send the frame
(and take ownership of it), or return an error, and return it back
to net80211. Currently net80211 will just free the mbuf and node reference
and return, but drivers should not assume that.
The mbuf passed in will be either a single 802.11/802.3 frame in an mbuf,
or a list of 802.11 fragments chained by m->m_nextpkt. If the driver
has not set IEEE80211_FEXT_SEQNO_OFFLOAD then the packet will have
a sequence number assigned which the driver can fetch via M_SEQNO_GET().
The mbuf also holds an ieee80211_node reference.
(Note that fragments do not have sequence numbers assigned nor node
references.)
The driver needs to do a few things with this frame. Notably if it's
an 802.3 offload device, it will be handed an 802.3 frame with no
802.11 information. In that case, the driver just needs to queue
it for send to the hardware/firmware.
For devices which accept 802.11 frames, a few things are needed:
* It needs to queue them for send, in the order they're given.
* If there are any reasons the frames need to be buffered in the
driver - eg node power state, asynchronous node/key/state updates -
then they'll be buffered here until needed.
* It needs to do any local hardware/firmware setup - rate control,
transmit configuration, destination queue decisions, etc.
* Hardware/firmware typically has some way to mark a frame as a type
(control, data, management), whether RTS/CTS is needed,
* If IEEE80211_FEXT_SEQNO_OFFLOAD is set in the driver, it may need to
allocate 802.11 sequence numbers via a call to ieee80211_output_seqno_assign().
* If the frame is part of an MPDU (m->m_flags & M_AMPDU_MPDU) then
the frame may need to be handled differently. (For example rtwn(4)
leaves sequence number assignment up to the firmware when A-MPDU is
enabled.)
* If the mbuf is marked as needing encryption (IEEE80211_FC1_PROTECTED
is set in the 802.11 header) then the frame needs to be encrypted
with the current encryption state via a call to ieee80211_crypto_encap().
* Finally, the frame is queued to the hardware/firmware.
Again it is critical that the 802.11 sequence number and encryption be
called together in the same order. This is typically done by the TX work
being done in a lock, or all frames being pushed into a single software
TX queue.
### Data path vs control path and the need to buffer frames
net80211 currently treats encryption key programming, VAP state
and other updates as synchronous calls. For example, the
transmit path will call the driver to add a node, then
set the encryption keys and then queue a frame to be transmitted.
For devices which are programmed directly with no queued operations
(such as the ath(4) devices) the encryption key and node programming
is immediate. However, for many other devices - firmware and
USB are two examples - these operations are asynchronous.
And these code paths tend to be in the transmit paths from
upper layers that may have locks held, so sleeping is not an option.
So for now this needs to be implemented in the driver itself.
It will need to maintain a per-node queue of transmit frames;
it will need to track asynchronous node creation/updates and
encryption key updates and buffer transmit frames for a node
until the node add/update and encryption key add/update is
completed.
### Transmit Completion Notifications
The net80211 stack may request a completion notification
to be called when a transmit frame has completed.
This will be done via a call to ieee80211_add_callback().
It is used in various parts of the net80211 stack to
drive the MAC state machines - for example, being notified
once an BAR (Block-ACK request) frame has completed so
the retry timer can be cancelled.
This requires that mbufs that are transmitted with a requested
completion callback be checked and handled appropriately.
This is covered in the next section.
### Completing and freeing transmit path mbufs
There are two paths to freeing mbufs - ieee80211_free_mbuf() and
ieee80211_tx_complete().
#### Before transmit - ieee80211_free_mbuf()
ieee80211_free_mbuf() is used in drivers and net80211 to free
a list of mbufs as part of the transmit path setup so it can
properly account for and free an 802.11 MPDU / 802.3 frame,
or a list of mbufs representing 802.11 fragments. It doesn't
handle the ieee80211_node reference as at the early stage
of transmit there is a single ieee80211_node reference
covering all of the fragments being passed to the driver
for transmit.
If you're not supporting 802.11 fragment transmit (and you have
to register your driver with the IEEE80211_C_TXFRAG capability
to even support this) then you can ignore all of the above
and just not call ieee80211_free_mbuf() for now.
This must not be used for receive mbufs. Yes, this is not
well named and should likely just be renamed.
#### After transmit queueing / attempts - ieee80211_tx_complete().
In the general case of an transmit mbuf being completed (either
successfully or unsuccessfully) net80211 provides a call
to handle everything - ieee80211_tx_complete(). This takes
the relevant destination node (struct ieee80211_node),
the mbuf, and a status indiciating success or failure.
A call to ieee80211_tx_complete() handles a variety of
common functions:
* It increments the ifnet counters as appropriate;
* If the frame has a TX completion notification callback attached
it will process said callback;
* If a node is supplied then the node reference is freed
In the past some drivers implemented the mbuf TX callback
handling themselves, resulting in some drivers supporting
callback and some drivers not supporting callbacks. The goal
here is to implement a single way for completions to be
handled.
Note that some hardware / firmware do not support per-frame
completion / status notification. For example, USB devices
tend to not send individual notifications for frames - you
may be able to request it for specific frames, but the
status notifications are expensive. In these cases, drivers
may just call ieee80211_tx_complete() with a status based
on whether the frame was queued to the USB endpoint successfully
or not.
#### Atheros Fast Frames / 802.11n A-MSDU transmit
(Note this is purposely short - a larger write-up for this will be
done on a separate page.)
The transmit path above will call ieee80211_ff_check() and
ieee80211_amsdu_check() to see if the given node/frame should be
queued for an Atheros Fast Frames MPDU or an A-MSDU.
If the frame should be queued it will be queued locally and NULL
will be returned; if there's already a frame queued it may be
paired with a queued frame and both returned as a single mbuf / MPDU
to send.
As far as the driver is concerned, it will be handed a single
802.11 MPDU to send.
#### 802.11n A-MPDU transmit
net80211 implements the A-MPDU negotiation and block-ack request/response
handling. However currently the driver must implement A-MPDU packet
queuing, buffering, submission and retransmission.
There are some methods that the driver can override to control the
A-MPDU transmit negotiation flow (ic->ic_addba_request, ic->ic_addba_response,
ic->ic_addba_response_timeout, ic->ic_addba_stop) and the Block-Ack
response completion or error/timeout (ic->ic_bar_response).
#### Driver queue completion
Currently there are two things a driver should do when its own queues
are (mostly) empty:
* When the transmit queue is empty or mostly empty, call ieee80211_ff_flush()
to flush out any pending A-MSDU / Atheros Fast Frames to be transmitted;
* When the receive queue is being handled, call ieee80211_ff_age_all() to
flush out any frames that are older than a provided time interval.
These calls ensure that any queued frames in Fast Frames / A-MSDU queue
don't stay in there permanently.
### Non data frame transmission (management, control, action, beacon, etc)
Non data frames are sent via ieee80211_raw_output(). The main exception to
this is beacon frames, which are separately initialised and pulled from
net80211 into the driver by the driver specific beacon handling routines.
Raw frames differ from data frames in a couple of ways:
* Transmit parameters are typically sent from userland or the caller
(struct ieee80211_bpf_params \*), and
* The input path into the driver is via ic->ic_raw_xmit(), not ic->ic_transmit().
The driver can combine the data and non-data paths into a single path.
The main reason for keeping these separate is to cleanly support drivers
and firmware which allow 802.3 frames to be sent and received, but still
need a side channel to send and receive management frames for various other
functions.
The raw frame output path is used by:
* The BPF output path - ieee80211_output() ;
* The management frame output path - ieee80211_mgmt_output() ;
* The NULL data output path - ieee80211_send_nulldata() ;
* Sending probe requests - ieee80211_send_probereq() ;
* Sending probe responses - ieee80211_send_proberesp() ;
* Sending 802.11n BAR frames - ieee80211_send_bar() ;
* .. and anywhere where the individual protocol (eg 802.11s) wishes to send raw
non-data frames.
This path is not REALLY designed for high speed data - for example,
it should work for basic packet injection, but it does not pass through
the normal functions for encryption, power save, TX aggregation and other
data specific operations. It expects to be handed a raw, already encapsulated
802.11 frame.
Note this is not an 802.11 MPDU - this is an 802.11 frame. For example,
non-data frames may not have sequence numbers. NULL data frames have a sequence
number but that sequence number must be 0.
Once the driver ic->ic_raw_xmit() call is made, the driver can handle the
802.11 frame in any way it sees fit. Again, it can't assume it's an 802.11
data frame.
### BPF path
Control frames are injected from userland and net80211 via a raw transmit path,
separate from the data path. This dates back to the earliest Orinoco/WaveLAN
cards, where the earlier firmware only allowed 802.3 frames to be sent/received,
but later firmware introduced raw packet transmit to allow wpa_supplicant
operation.
Packet injection begins via the BPF/radiotap input path. The code in
ieee80211_radiotap.c attaches a BPF operator to the VAP during the
call to ieee80211_radiotap_vattach().
Raw frames start in BPF and are queued via bpf_ieee80211_write(), which will
send the frame into the driver via a call to the VAP ifp->if_output() and then
if provided, a copy of the feedback mbuf via the VAP ifp->if_input().
The ifp->if_output() method by default is ieee80211_output(). The driver
can override this. This takes care of validating that it is an 802.11
frame, extracts the (struct ieee80211_bpf_params \*) header from the
destination sockaddr passed in via BPF, finds the relevant
struct ieee80211_node \*) tx node, grabs a reference, some further sanity
checks and then calls ieee80211_raw_output(). The rest of the raw output
path is the same as net80211 sourced raw frames.
### Power Save Management
By default, net80211 will track legacy power-save state between IBSS nodes
and STA <-> AP nodes (ie, full node buffering via the power management bit
in the 802.11 header; TIM/ATIM bitmaps in beacons, NULL data frames to wake up)
and PS-POLL frames being sent by stations to request individual frames.
The transmit path will pass frames destined to asleep stations to the power
save queue via a call to ieee80211_pwrsave().
There are a number of VAP methods for the driver to tie into if it needs to be
informed about this state (vap->iv_set_tim, vap->iv_recv_pspoll, vap->iv_node_ps).
These allow the driver to keep its own internal state in sync with net80211
and allows it to better maintain its own transmit queue state.
See the ath(4) driver for a comprehensive example of how these methods are used
to correctly transmit and buffer frames from an AP to STA device without packet
loss.
### Transmit path encryption
The net80211 stack needs to handle a variety of transmit encryption schemes
based on all the combinations that driver and firmware interfaces may require.
In general, the transmit encryption is done in two phases:
* In ieee80211_encap(), the transmit key is chosen via a call
to ieee80211_crypto_getucastkey() or ieee80211_crypto_getmcastkey() - the
key index is added to the 802.11 header and space is reserved between
the 802.11 header/payload and at the end for the encryption key data to be
added;
* Then when the driver transmits the frame, it calls ieee80211_crypto_encap()
to actually do the encryption.
Some hardware will completely offload encryption, so although the key choice
is made, various driver configuration options are set to inform net80211 not
to add all the padding. Others will offload encryption but require the
space to be provided in the frame for the hardware/firmware to add the
encryption information into.
### What is IEEE80211_F_DATAPAD ?
This is actually to support hardware such as the Atheros 802.11abgn chips,
which have a 4 byte alignment requirement between the 802.11 header and
the data payload (including the encryption parts.)
Yes, it likely should be a more generic option.
### Future work
* It would be nice to more formally define and enforce what drivers should be
doing with mbufs during the whole transmit lifecycle of an mbuf.
* Perhaps add a function or two for the drivers to use to
query whether a given mbuf has a TX notification attached (rather
than drivers querying M_TXCB) so they can individually
register for explicit notifications so they can provide more
accurate completion information.
* The fast frames age / flush routines should really be expanded to
be required functionality in net80211 drivers rather than optional
when IEEE80211_SUPPORT_SUPERG is enabled, so further software transmit
queue management is possible in net80211.
+101
View File
@@ -0,0 +1,101 @@
# Debugging in net80211
This document describes how debugging is implemented in net80211.
## Overview
net80211 has run-time configurable debugging available. It is configured
per-VAP. It is implemented as a bitmask which can be controlled via a
sysctl at runtime.
Debugging is compiled in when IEEE80211_DEBUG is defined.
There is currently no global debugging API available; top-level net80211
code is typically using printf() or some wrapper around it (eg
net80211_printf).
The debug API is defined in (ieee80211_var.h). This includes the
debug field definitions and exported debugging API. The actual implementation
of the debugging routines is currently in (ieee80211_input.c) - see
(ieee80211_note) for an example.
The bitmap of available debugging sections is in (ieee80211_var.h), prefixed
with IEEE80211_MSG . See (IEEE80211_MSG_DEBUG) for an example.
## Usage
Calls to the debugging APIs should not include a terminating '\n' character.
This will be added by the debug call.
The simplest example is a call to IEEE80211_DPRINTF(). This takes a vap
pointer, which debug option to log to, then the format string and optional
arguments. For example:
```
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N, "%s: called!", __func__);
```
The debug flags can be combined together using bitwise OR so they are
emitted if one or more debug options are set, for example:
```
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N | IEEE80211_MSG_ASSOC,
"%s: called!", __func__);
```
There are a number of different debugging calls that are designed to be
used in different contexts. Although they all currently end up printing
to the same debug output, keeping them separate allows for future
behavioural changes whilst minimising rototilling the whole codebase (eg
allowing non-DPRINTF to turn into event tracing.)
* Straight up debugging should be done through IEEE80211_DPRINTF() .
* Debugging that's related to a specific ieee80211_node (eg a state
change for a specific node) should be done via a call to IEEE80211_NOTE() .
* Debugging that's related to a specific ethernet MAC address (eg
scan results) should be done via a call to IEEE80211_NOTE_MAC() .
* Debugging that should include a frame header should be done via
a call to IEEE80211_NOTE_FRAME(). Note this takes a (struct ieee80211_frame \*)
pointer.
* Debugging involving discarding frames (eg invalid frames) should be
done via a call to IEEE80211_DISCARD() .
* Debugging involving discarding frames due to an invalid / bad IE should
be done via a call to IEEE80211_DISCARD_IE().
* Debugging involving discarding frames due to a MAC address (eg ACL failure)
should be done via a call to IEEE80211_DISCARD_MAC().
## Usage Notes
* It is required that the debugging be compiled in/out purely by defining or not
defining IEEE80211_DEBUG. This can often trip up unused variable warnings
when debugging is disabled, so just double-check both configurations.
* It is important to ensure calls to the debugging (and any other logging API)
do not change any state/variables. For example, do not call a function that
updates some counter or some state variable inside a call to IEEE80211_DPRINTF().
It won't be called at best and it will just be compiled out entirely at worst.
## Configuration
* The 'vap->iv_debug' field is controlled by the OS specific module.
* In FreeBSD (ieee80211_freebsd.c) it is assigned a sysctl (net.wlan.X.debug)
during (ieee80211_sysctl_vattach).
* FreeBSD ships the wlandebug(8) tool to query and set this at runtime.
## Implementation Details
* The debug API goes out of its way to do the debug flag check before evaluating
function parameters and potentially assembling the logging output. See
(IEEE80211_DPRINTF) for an example.
## Future work
* Top-level net80211 debugging APIs and control would be nice (for things
that are not specific to a VAP.)
* Drivers end up having to implement their own debugging API; it may be nice
to provide drivers a net80211 API to do their own driver specific logging.
* The debug macros should likely be refactored out to a new header file,
separate from ieee80211_var.h, so they can be more easily referenced.
* The debug fields should likely be refactored out into a new separate header
file that is designed to be consumed both by the kernel and by userland
utilities wishing to query/set the debug bitmask.
+563
View File
@@ -0,0 +1,563 @@
# 802.11 protocol overview
This is a quick overview of the 802.11 protocol and where it intersects with
net80211. It is not intended as a comprehensive deep dive into all of 802.11.
TODO: link to appropriate sections in 802.11-2016 / 802.11-2020 depending upon
which PDF is freely available.
## 802.11 overview
The 802.11 protocol / specification is a very large document which covers
everything from the raw signals going out over the air up to how devices
need to behave in different operating modes.
The IEEE specification documents and amendments describe what devices should
and must do in order to interoperate. It's important to note that the
intersection of "what the standard says" and "what devices do" is not always
fully aligned. The 802.11 specification has evolved over twenty-five years
and for the most part this allows interoperability between the original 802.11b
hardware and modern multi-band 802.11ax devices.
It's also important to note that 802.11 is not just limited to the IEEE
specifications. 802.11 devices are almost exclusively RF devices (if you
read the specification you may find the old infrared / IR protocol definition!)
and so need to operate inside of the radio regulatory rules defined by each
country. These define a wide variety of RF environmental behaviours
including frequencies can be used, when devices can transmit, what transmit
power is allowed, interoperability with other devices (802.11 and non-802.11)
and radar interoperability. For the purposes of this document these will
be called "regulatory concerns" and will be covered elsewhere.
The 802.11 specification breaks things up into a handful of top level areas:
* the PHY layer - how the device interfaces with the RF environment and
encodes/decodes RF transmissions into data streams.
* the MAC layer - defines how data is packetized into individual data frames,
exchanged with the upper layer (ethernet/bridge), deciding when and what
to transmit via the PHY layer.
* MLME - (MAC layer management entities) - defines all of the state methods
and transitions that underpin the 802.11 MAC state machine.
* Security - the cipher and key management components.
* PHY specifications - the specific implementations of PHYs - 2GHz DSSS
(spread spectrum), 2GHZ CCK, OFDM, ERP, 802.11n / HT, 802.11ac / VHT, etc.
Most 802.11 implementations do not implement a 1:1 definition of each of these
layers - notably implementing every single MLME state would be a huge amount
of work.
## 802.11 revisions
There have been many revisions of the 802.11 specification. The specifications
can be found online at https://www.ieee802.org/11/.
The latest specification being implemented in net80211 is 802.11-2020, however
net80211 is far from completely compliant. Generally new code which implements
802.11 features / protocol handling should identify the specification and
section which it is referencing.
## 802.11 protocol and frame definitions
net80211 keeps most 802.11 frame and protocol definitions in a single location
(ieee80211.h).
This contains descriptions of the 802.11 frame and field definitions, ranging
from the lowest definition of the frame itself up through frame types/subtypes,
individual field definitions, information elements, action frames, and
anything else that can be found in the 802.11 specifications.
The PHY definitions can be found in (ieee80211_phy.c) and (ieee80211_phy.h).
Notably those include the frame timing information useful for rate control
and frame duration calculations.
## 802.11 Revisions
(TBD)
### Legacy 802.11
The earliest 802.11 devices implement 1Mbit/s and 2Mbit/s direct spread spectrum
frames. These include the earliest Wavelan devices. These are grandfathered
into 802.11b. The PHY specification can be found in 802.11-2020 Section 15.)
### 802.11b
802.11b devices implement Section 15 (1Mbit/2Mbit) PHYs as well as the high
rate DSSS specification (802.11-2020 Section 16) to provide 5.5Mbit and 11Mbit
CCK rates. They interoperate with legacy 802.11 devices by using compatible
PHY encodings and will limit their performance if said legacy devices are
detected.
### 802.11a
802.11a devices implement OFDM rates from 6Mbit/s to 54Mbit/s on the 5GHz
band. Among other features, it also defines 5MHz and 10MHz wide channel
behaviour. This is covered in the OFDM PHY specification (802.11-2020
Section 17.)
### 802.11g
802.11g devices implement OFDM rates from 802.11a, the CCK rates from 802.11b
and the DSSS rates from legacy 802.11. These are covered in the ERP
specification (802.11-2020 Section 18.) There are some MAC extensions for
negotiating 802.11b / 802.11g interoperability and these are documented
throughout the MAC specification. This also specifies support for 5MHz and
10MHz wide channels.
### 802.11n (HT)
802.11n introduced a variety of high throughput rates and feature support
(hence why it's called HT - high throughput). It introduces higher density
OFDM rate encodings, 20 and 40MHz wide channels with interoperability for
earlier devices, packet aggregation via A-MPDU and A-MSDU, MIMO (multiple input,
multiple output spatial streams), some initial beamforming support, power
saving extensions and more.
The physical layer support is covered in the HT PHY specification (802.11-2020
Section 19.) The rest of the MAC extensions are documented throughout the
rest of the specification.
### 802.11ac (VHT)
802.11ac extends the 802.11n specification (hence why it's VHT - Very
High Throughput) and boosts performance by adding higher density OFDM QAM
encoding (256-QAM), wider channels (80MHz, 160MHz), split 80+80MHz channel
support, much larger A-MSDU / A-MPDU frame sizes, support for MU-MIMO
(multi-user MIMO) allowing APs to transmit to multiple STAs at the same time
and various other extensions.
It builds on top of the 802.11n MAC and PHY specification, so a lot of
802.11n feature and MAC negotiation happens as part of 802.11ac negotiation.
The PHY layer is covered in the VHT PHY Specification (802.11-2020 Section
21.) Again, the rest of the MAC extensions are documented throughout the
rest of the specification.
### Greenfield versus backwards compatibility
The various protocols supported by 802.11 build on top of earlier protocols.
So typically you're not building a single implementation for each protocol -
for example, you can't handle 802.11ac support without implementing a large
amount of 802.11n support.
(As a side note, the 802.11 frame has a protocol version field, and
that actually changed in 802.11ah (900MHz and longer distance bands) -
which changes a lot of what the fields do. No, net80211 currently does not
support 802.11ah and will drop frames whose 802.11 protocol ID is not
supported.)
At the PHY layer, later model hardware can transmit data encodings which
earlier model hardware just won't recognise. All they'll see is an increase
in RF power on the channel at best and signals that will confuse the
RX decoder / cause hardware issues at worst.)
So each of the PHY specifications will lay out a few things:
* How frames should be encoded in the air in a way that earlier
hardware can decode them enough to know it's not for them;
* How devices can identify that earlier protocol devices are around and
change the configuration (eg STA changing its own configuration,
AP changing the configuration of the network it controls, etc)
to provide backwards compatibility.
These come at a performance cost. For example, an 802.11g AP which
supports 802.11b and 802.11 devices needs to notice that an 802.11b
device wishes to associate, and when it sees this, change some of
its configuration (notably "long preamble" so 802.11b devices can
decode frames that are being transmitted, whether destined to it or not.)
Various devices allow backwards compatbility to be configured.
For example, an 802.11n AP may be configured to deny non-802.11n clients.
This may improve performance but then earlier clients can't connect.
In 802.11n deployments this was known as a "greenfield deployment".
This typically disables any and all pre-11n interoperability at both
a MAC and PHY layer. net80211 has some flags for this to specifically
inform devices that they can configure the hardware for such a setup.
Not all drivers implement it however, and in a lot of cases they will
still handle pre-11n framing, even if the net80211 code will deny
association.
There are other components to backwards compatibility which are worth
keeping in mind when reading through the 802.11 specification and
net80211 stack / driver code. These include:
* short/long preamble - (vap_update_preamble)
* short/long slot time configuration - (vap_update_slot)
* 802.11g protection mode (vap_update_erp_protmode) -
whether to use CTS-to-self around each transmission
* 802.11n protection mode (vap_update_ht_protmode) -
whether to use RTS/CTS around each transmission
* 802.11n 20/40MHz BSS operation (whether an 802.11n AP sees other APs that
overlap its frequency range and need to reconfigure how to protect
transmissions)
## How 802.11 (very briefly) works over the air
This is a very brief and not at all comprehensive overview of how 802.11
works over the air. The goal of this section is to provide enough background
information to help de-mystify reading the net80211 stack and wireless
driver source.
### Why there's timing requirements in the first place
Each of the PHY sections in the 802.11 specification describe what
the PHY needs to do in order to transmit and receive data. It's not
anywhere as easy as "toggle some bits on a wire".
An important thing to understand is that hardware isn't immediate.
All the state machines in your 802.11 devices take non-zero time
to make decisions about when to transmit, when to receive, locking
onto a signal, deciding it can be decoded, getting reset for the next
frame, etc.
So a lot of what you'll see in 802.11 negotiation and feature support
is linked to the underlying hardware implementations and limitations
of the time. For example the 802.11b specification defines the slot time
as 20uS, but the 802.11g specification lowers it to 9uS. The "slot time"
value defines the unit of time used for contention management / backoff, and
it's defined partly by what the speed of light dictates (ie how big
of a physical area you want to be able to "hear" in determining if the
area is busy) and how quickly the hardware can guarantee to respond.
It dropped to 9uS because hardware got better, but to interoperate
with older devices without starting to transmit before they're
ready to react, 802.11g devices will fall back to 20uS slot time when
they detect an 802.11b device.
This carries through everywhere in odd places that you're not necessarily
aware of. For example, the 802.11n A-MPDU definition includes negotiated
padding between frames and limits encryption ciphers (typically CCMP or
GCMP.) This is due to hardware support - the MAC may be able to support
much less padding when no encryption is used, but setting up / resetting
the encryption / decryption blocks may take more time and thus larger
A-MPDU padding values are negotiated.
### Wait, the speed of light?
Yes. The speed of light is roughly 300 metres for each microsecond of
travel time.
### Preambles, SIGs, PLCP, sending actual data and waiting / slot times
There are a few things that are worth understanding at a high level.
* The first thing that a device needs to do is determine
whether the air is busy or free. There'll be some hardware
to determine the signal level versus noise floor and provide
a signal to the transmit hardware that the air is free,
and to the receiver that it may want to try start decoding
something.
* The receiver needs to get in synchronisation with the transmitter.
This is a one way operation - the transmitter needs to transmit
enough of a signal that the receiver can "lock onto" and get itself
ready for further data. This is called the "preamble" - it's
typically a low bitrate repeating pattern of data that gives
the receiver hardware time to lock onto, figure out the signal
level and be ready for the next phase.
* Note that the receiver may pick up the preamble at any point in its
transmission so it can't guarantee it will see exactly "x" bits of some
repeating pattern.
* Then there's other bits and pieces - eg look for L-SIG, HT-SIG
in the PHY documentation - which is used to further synchronise
what's about to happen.
* Finally it will start transmitting the PHY framing bits needed to
identify what the upcoming transmit rate and configuration is
(all the stuff leading up to the PLCP header, then the PLCP header.)
Things get more complicated with MIMO, MU-MIMO, 802.11ax OFDM-A, etc but
don't worry about those for now - they build on top of all of these
ideas.
Once the data is transmitted, there's some quiet time between frames
before the receiver can ACK (and then a period of time where an ACK
is expected.) The transmitter needs to finish transmitting, then
reset its internal state back to idle to be ready to receive - and
there's the pesky speed of light speed of 300m per microsecond -
so there's some MAC (interframe spacing) and PHY (slot time) enforcing
quiet so everyone has a chance to receive the frame and the reciver
gets ready to receive. Then if there's an ACK, the ACK happens.
### PLCP header
Once all of the preamble, SIG/training stuff is done, the transmitter
will send a PLCP header with information about the transmitter
type and rate (and that's very handwaving it.) net80211 has definitions
for the plcp header (ieee80211_plcp_hdr) but it's highly unlikely it will
be relevant or available in modern devices.
### How data is encoded - encoding rates, symbols, guard intervals
Now, once the transmitter has sent all of that, it will start to send
actual data encoded at the desired transmit rate. The data bits
that you're transmitting go through a variety of encoding schemes
before they're turned into bits that are clocked out at the 802.11
physical layer (think "forward error correction" as an example),
but they're turned into what are known as "symbols".
A symbol can be thought of as a group of bits encoded in one specific
RF representation. Explaining all the details isn't in scope of this
document (and I encourage interested parties to do a quick dive
into information theory!) but there are a couple of important higher
level concepts to understand here that influence what happens
later on in packet delivery.
For OFDM encoding:
* Each symbol is preceded by a quiet time called a guard interval, to make
sure any reflections don't interfere with the upcoming symbol;
* Each symbol is then transmitted for a specific length of time to make sure
it's received by everyone inside the desired area (again light = 300m/sec
per microsecond);
* All symbols for a given 802.11 MPDU are sent at the exact same rate;
* This is repeated until all the symbols are transmitted.
The higher the data rate used, the higher the signal level needs to be
and lower the tolerance it has to interference. Forward error correction
can only do so much, and the higher throughput rate encodings sacrifice
FEC for throughput.
Once an uncorrectable error occurs and the frame fails CRC, the whole MPDU is
dropped by the receiver.
Part of why A-MPDU is so important for high throughput is that the
errors are limited to a single MPDU in the burst of MPDUs. Ie, if
the transmitter sends ten MPDUs in a single A-MPDU, and five of them
have uncorrectable errors, then five .. well, didn't. This means
the receiver can ACK some but not all of the MPDUs, and the transmitter
can re-send those with new MPDUs.
The default guard interval is 800ns. 802.11n allows for negotiating
shorter guard interval (400ns) which can be done per device in a BSS.
An 800ns guard interval is a little short of 300 metres, and 400ns is
a little short of 150 metres - so using short guard interval means
you trade increased performance for potential decreased performance
if you have reflections or stations more than 150 metres away.
802.11ax adds support for 1.6us and 3.2us guard intervals for physically
larger deployments.
### MAC layer framing
The MAC layer handles data that is encapsulated in the given transmit
rate that was established in the PHY (PLCP) header. This includes
the 802.11 MAC header, CRC trailer, any of the cipher processing that
happens in between. In the case of 802.11n, it can encapsulate
multiple frames being sent back to back in a single transmission.
Devices which do partial / no offload will typically produce and
consume 802.11 MAC layer frames to the driver and net80211.
It's thus important to understand MAC framing and frame types.
### MPDU versus MSDU
An MSDU (MAC service data unit) is an individual frame (think "802.2/802.3
ethernet") passed from the network stack into net80211.
An MPDU (MAC protocol data unit) is one or more MSDU frames wrapped by an
(ieee80211_frame) header and CRC trailer. It is what is eventually
encapsulated inside the PHY framing (preambles, training symbols, PLCP
header, etc) and sent over the air.
Notably an 802.11 MPDU isn't just an IPv4/IPv6 frame with an 802.11
header/trailer - it is a full ethernet frame that is being wrapped
by 802.11 framing.
### Tracking airtime with NAV
802.11 devices have to interoperate in a shared medium. Earlier protocol
definitions require one transmitter at a time. Later specification
devices (MU-MIMO with 802.11ac, OFDM-A with 802.11ax, etc) introduce the
ability for devices to transmit and receive simultaneously.
The simplest way to track this is with NAV (network access vector.)
The NAV implementation in pre-11ax devices is a single counter which
counts down to zero. Once it is zero, the air is considered "available"
to attempt to check to transmit on. The transmitter will also check
whether the air is busy (ie can it detect any signals present) before
it transmits - this is called CCA (clear channel access) and is
typically implemented in hardware.
The duration field in (ieee80211_frame) is a microsecond field which
covers the whole duration of the frame being transmitted. Receivers
that decode the frame - even if it's not destined to them! - will listen
to the NAV and add it to their own NAV.
All 802.11 frames have a duration field.
### Fragmented frames
(TBD)
### Sequence counters and duplication detection
(TBD)
### EDCA and QoS
(TBD)
### Inter-frame spacing (IFS)
(TBD)
## 802.11 frame layout
An individual 802.11 frame contains frame control (version, type, subtype),
duration, addressing, sequence number and optional QoS information.
The basic definition is available at (ieee80211_frame) but other definitions
are also possible - (ieee80211_qosframe), (ieee80211_frame_addr4),
(ieee80211_qosframe_addr4).
It then has a 4 byte CRC32 trailer appended at the end.
### Addressing types and traffic direction
(TBD - 3addr, 4addr, each of the fields, etc)
### QoS versus non-QoS frames
(TBD)
### RTS/CTS exchange and airtime
(TBD)
### CTS-to-self / OFDM protection
CTS to self is a concept introduced in 802.11g. The general idea is that a
transmitter can send a CTS to its own MAC address for the duration that it
wishes to transmit for. Since the CTS frame is transmitted at a slower
legacy rate, it both reserves airtime in any receiver in earshot, and
it is understood by older 11b only devices which do not understand 11g.
This ends up also being useful for 11n, 11ac etc to interoperate with
earlier devices, but they typically rely on a normal RTS exchange.
### Data frames
(TBD)
### Management frames
(TBD)
### Control frames
(TBD)
Notably, control frames do not have a sequence number and so can't be
de-duplicated.
### Action frames
(TBD)
## Frame combinations
There are various ways that 802.11 frames are combined together to improve
performance.
### ACK, Delayed Block-ACK, Immediate Block-ACK
(TBD)
### Atheros Fast Frames
(TBD)
### A-MSDU
(TBD)
### A-MPDU
(TBD)
## Security / Encryption
This is a much larger topic, however it's worth touching on the basics here to
understand how frames are redirected into the security/encryption paths in
net80211 and what devices may do with said frames.
### WEP, IV header and keys
WEP is an obsolete encryption method dating back to the earliest 802.11b
specifications. It involves a 4 byte header which includes
* a 24 bit IV (initialisation vector);
* a 2 bit field indication which of four keys to use;
* A CRC at the end.
It's relevant today because later cipher frame formats still use the IV
header - they're just extended to include more information. Notably, the
four key indexes are typically implemented and used in hardware, and have
different meanings depending upon the kind of traffic being handled.
### WPA/WPA2 management
This is handled in userland. The 802.11 specification covers everything
involved in key exchange and management but it's out of scope for this 802.11
overview documentation.
### CCMP, GCMP, TKIP frames
These later ciphers still use the WEP header, but they then add extra bytes
to it to include the larger sequence number space, other options needed
for said ciphers, and a larger trailer for CRC and TKIP MIC.
### IV duplication / tracking
net80211 tracks the received IV / sequence number for each station indexed
by QoS TID. Anything with an earlier IV is discarded as a stale packet or
potential replay attack. See the ni_txseqs[] and ni_rxseqs[] field in
(ieee80211_node).
Note that the 802.11 layer sequence number field will apply /first/. Traffic
which the 802.11 input layer thinks is old or retransmits will be discarded
before handed to the net80211 crypto routines.
### Unicast vs Group Keys
WEP has four global keys which are shared between all devices wishing to
communicate. The keys are provided in the WEP header.
However for later ciphers the four key indexes start taking on new meanings.
Notably key index 0 is the "unicast key" which handles traffic for a given
station and is unique for that station, and keys 2 and 3 are used for
group keys - shared keys for broadcast traffic that all stations need to be
able to decrypt.
(key 1 is also used for unicast station traffic for seamless station key
updating, but net80211 currently doesn't support this extension/feature.)
There's also upcoming work for encrypted management traffic and encrypted
beacons which reuse the key indexes for their traffic, but then don't treat
them as "global keys" - they start being treated as "global keys but only
for this traffic type."
It's important to understand the difference between global keys (WEP) versus
group and unicast keys (everything else) when looking through the net80211
data and encryption handling paths.
## 802.11 Operating Modes
(TBD)
### Station
(TBD)
### Access Point
(TBD)
### IBSS / Ad-Hoc
(TBD)
### Mesh / 802.11s
(TBD)
+139
View File
@@ -0,0 +1,139 @@
# net80211
This is the 802.11 wireless stack for FreeBSD.
## Introduction
The net80211 subsystem implements the 802.11 protocol and support infrastructure.
It supports a variety of device types, 802.11 protocols, operating modes and
security extensions.
net80211 handles the 802.11 state machine, interface management, node management,
virtual interfaces, packet encapsulation and de-encapsulation and basic
security key management.
The userland ioctl() API provides control mechanisms for the above and is how
management tooling (ifconfig, libifconfig) and management services (hostapd,
wpa_supplicant) interfaces with the net80211 stack.
The security state machine and key management (802.1x, WPA, etc) are handled
by management services.
Drivers can implement as much or as little of the 802.11 infrastructure as
needed. net80211 support drivers from full-offload (ie, supplying ethernet
encapsulated/de-encapsulated frames with management control via driver
methods) down to fully software controlled devices (ie, the hardware
is minimal and all 802.11 packet handling, state machine, reordering, security,
etc is handled by net80211.)
## Overview
net80211 consists of a few top level design modules:
* The 802.11 device representation and functions (ieee80211com), used
in conjunction with an 802.11 device driver to represent the physical device.
* The 802.11 virtual interface representation and functions (ieee80211vap),
used to represent instances of virtual interfaces.
* A representation of 802.11 stations/nodes (ieee80211_node), which
keep the state of each 802.11 station/node that the stack knows about.
* Encryption handling (ieee80211_crypto), handling 802.11 frame encryption,
decryption and session/state tracking.
* Regulatory domain (ieee80211_regdomain), which implements the 802.11
regulatory domains, allowed frequencies, operating modes and transmit power.
* Radar detection (ieee80211_dfs.c), tracking the state of radar detection and
interoperability in the 5GHz frequency range.
* Transmit rate control (ieee80211_ratectl.c) implements software and
firmware based rate control for devices that don't implement full rate control
offload.
* Power save support (ieee82011_power.c) implements various power saving
mode features and support for devices which do not fully implement offloaded
power management.
* Operating system specific interfaces (ieee80211_freebsd.c) which implement
the bulk of the operating system specific glue (logging, memory allocation,
network interfaces, etc.)
* The configuration interface (ieee80211_ioctl.c) which implements the ioctl
API used by userland to configure and monitor the state of the 802.11 stack
and devices.
In addition, each operating mode (adhoc, station, AP, WDS, mesh) have their own
modules that implement the state machines and functionality required for 802.11.
## Portability
Although net80211 attempts to keep most OS specific components in a single file
(ieee80211_freebsd.c), it is not currently perfect.
Notably:
* There are still plenty of FreeBSD-isms located throughout the source code,
including BSD specific includes, network APIs, etc.
* The interface and networking model is still very BSD, including using the
system implementation of mbufs.
When developing for net80211 please keep in mind that other operating systems
(such as DragonflyBSD) and third parties do leverage this codebase.
Try to keep all FreeBSD specific components in ieee80211_freebsd.[ch].
## Protocol Overview
A basic protocol overview is available at (@ref md_net80211_PROTOCOL).
The most comprehensive overview is the 802.11 protocol document itself,
but it is very large and implementations do not always correspond 1:1
with the protocol definitions.
## Functional Overview
(TODO)
* Module layout
* Logging
* Debugging - (@ref md_net80211_DEBUG)
* Top-level device layout (ieee80211com)
* Data / Control Path Overview (@ref md_net80211_DATAPATH_TRANSMIT), (@ref md_net80211_DATAPATH_RECEIVE)
* Deferred work
* Regulatory
* Virtual interfaces
* Operating Modes
* Nodes
* Node tables, node table iteration
* Device and VAP states
* Node states
* Operating modes
* Cipher management
* Radar detection
* ioctl interface
* ACL support
* Scanning, Scan Modules
* Power Management
* Transmit Path
* Receive Path
* A-MSDU Fast Frames
* A-MPDU
* Radiotap
* Monitor Mode
## Driver Overview
(TODO)
* Introduction
* Driver Structure
* Setup and Attach
* Virtual Interfaces
* Control Path
* Data Path
* VAP state
* Device State
* Suspend and Resume
+2 -1
View File
@@ -9,7 +9,8 @@ EXTRACT_ALL = YES # for undocumented src, no warnings enabled
#--------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = $(DOXYGEN_SRC_PATH)/net80211/ $(NOTREVIEWED)
INPUT = $(DOXYGEN_SRC_PATH)/net80211/
USE_MDFILE_AS_MAINPAGE = $(DOXYGEN_SRC_PATH)/net80211/README.md
GENERATE_TAGFILE = net80211/net80211.tag