Table of Contents
Some wireless implementation details
Network association
Before an 802.11 device can be used, it must become associated with a
network by exchanging a series of management frames with the Access
Point. In some cases there is additional security handshaking required
after association. gPXE's 802.11 layer handles this transparently by
always having the association task running if association has not yet
succeeded, and using the link-up bit in the wrapping net_device
to
indicate association status. Commands such as DHCP or autoboot will
therefore block on association success, and time out if an underlying
error condition rather than a transient failure is responsible for
association not working.
After association has succeeded, reassociation will be attempted upon a change in the SSID setting. It is not necessary to reassociate when the encryption key is changed, because association can only succeed with an invalid key on an open-system WEP network, and rekeying does not require reassociation in that case. If the user specified the encryption key erroneously at first, association will fail and be retried indefinitely, and will succeed soon after the correct key is provided.
Association is handled in a separate gPXE process, which runs through a series of states in predetermined order:
- First, all radio channels are scanned for beacon packets from the SSID specified in the network device's SSID setting. If no suitable Access Point is found, association restarts; if multiple APs for the same network are found, gPXE chooses the one with the strongest received signal strength indication. This is done using the
net80211_probe_*()
functions, which are exported to allow for user-level code likeiwlist
to use them as well. Once a good AP has been found, theNET80211_PROBED
bit is set indev→state
, and thedev→ctx
union should henceforth be understood to contain an association context rather than a probe context. - Assuming a suitable AP has been found, the
net80211_device
state (BSSID, ESSID, supported rates) is set to reflect it and an authentication packet is sent using the Open System authentication method. If the access point uses WEP and requires Shared Key authentication, it will respond with a failure indication and gPXE will attempt Shared Key authentication instead. If that fails as well, association restarts. Otherwise, theNET80211_AUTHENTICATED
bit is sert indev→state
. If the AP does not respond within one second, the authentication packet will be resent a few times. - Once authentication packets have been exchanged, gPXE sends an association request to the AP, which replies with an association reply. If the reply shows an error indication, association restarts; otherwise, we are now considered to be associated with the AP, and the
NET80211_ASSOCIATED
bit is set indev→state
. - After association, depending on the type of security for the network with which we are associating, it may be necessary to perform additional steps collectively termed “security handshaking”:
- If we are connecting to a WPA-Enterprise network, we will receive an EAP-Start packet. This is handed off to the appropriate EAP protocol handler, which negotiates authentication and provides a PMK to the WPA core.
- If we are connecting to any type of WPA network, we will receive an EAPOL-Start packet commencing the Four-Way Handshake, either immediately after association (for WPA-Personal) or after EAP-Success (for WPA-Enterprise). During the Four-Way Handshake, temporal keys are derived and installed to handle encryption and decryption for data traffic.
- Once security handshaking is complete, as indicated by a positive return from the security handshaker's
step()
function (see below), theNET80211_CRYPTO_SYNCED
bit is set indev→state
and the wrappingnet_device
is set link-up. Data traffic can now proceed. - WPA networks may periodically rekey, so the WPA core installs a protocol handler for EAPOL packets to handle such rekeying transparently.
Encrypted networks support
The gPXE 802.11 layer includes abstract support for encrypted wireless networks, as well as implementations of that support for WEP- and WPA-protected networks. We introduce two abstractions: a security handshaker and a wireless cryptosystem.
Security handshakers
A security handshaker is responsible for everything that must occur
before encrypted packets can be sent and received: deriving a master
encryption key from the user's specified net0/key
, securely
authenticating to the Access Point by some specified means, possibly
deriving more keys from the master key and using them instead of or in
addition to the master key, and specifying encryption keys and a
cryptosystem to use them for packet-level security. The currently
implemented security handshakers are “trivial”, WPA-Personal (PSK /
pre-shared key), and WPA-Enterprise (802.1X / EAP). The trivial
handshaker, used for WEP, just does some sanity checks on the
user-specified key and then installs it directly without a further
exchange with the AP. The WPA handshakers interface with a common WPA
core, supplying it with a pairwise master key derived either from the
user's passphrase (for Personal) or the EAP Master Session Key (for
Enterprise).
It is not possible to manually specify the security handshaker to be
used for a network; it is autodetected by _sec80211_detect()
in
net/80211/sec80211.c
. gPXE's linker tables are searched for a
handshaker of the requisite type, a new structure is allocated with
space for the requested amount of private data, and
dev→handshaker
is set to point at it. The dev→handshaker
pointer remains valid from the time the handshaker's init()
method
is called to the time its stop()
method is called.
Additional security handshakers are declared using gPXE's linker table
mechanism, by defining a structure of type net80211_handshaker
tagged with the _
_net80211_handshaker
attribute, containing
the following fields:
protocol
: one of the enumeration values ofnet80211_security_proto
indicating the type of network with which the handshaker should be used. For a new type of network it is necessary to modify the function_sec80211_setect()
innet/80211/sec80211.c
.priv_len
: the size of the private data area to allocate. When any of the methods below are called,dev→handshaker→priv
will point to this many bytes of dynamically allocated memory.init()
: a method called before the association process starts, to set up whatever is needed for association to succeed. In WPA, this setsdev→rsn_ie
to a pointer to the RSN information element that is to be advertised in the association request frame. In the trivial handshaker, this method installs the cryptosystem directly, andstart()
andstep()
areNULL
.start()
: a method called immediately before the association request frame is sent, to set up things so that security packets received in response to it will be handled appropriately.step()
: called continually by the association process as long as it returns 0. It is expected to process security handshaking packets, or monitor the progress of their processing by an asynchronous handler, and indicate when a definitive success or failure state has been reached.change_key()
: called whenever thenetX/key
setting might have changed, to update the cryptosystem with the new key if that makes sense for this type of handshaker. (It does for WEP/trivial, but not for WPA, because it's not possible to associate with the wrong key on WPA.)stop()
: called just before the handshaker is freed due to reassociation or closing of the 802.11 device. This method can free memory and clean up resources.
When a security handshaker has finished negotiating whatever handshaking is necessary, it will usually need to finish its job by setting up the 802.11 device to use some sort of cryptography on all future data packets. This is accomplished by the function:
#include <gpxe/sec80211.h> int sec80211_install ( struct net80211_crypto **which, enum net80211_crypto_alg crypt, const void *key, int len, const void *rsc );
Install the 802.11 cryptosystem of type crypt
for packets handled
by which
, which is &dev→crypto
for unicast RX and all TX
packets, or &dev→gcrypto
for broadcast RX packets. The encryption
key is len
bytes and is pointed to by key
. If applicable to the
cryptosystem in question, the initial receive sequence counter is pointed
to by rsc
with a cryptosystem-dependent length; NULL
can be passed
for an RSC of zero.
The currently defined values for crypt
are:
NET80211_CRYPT_NONE
: an open network with no securityNET80211_CRYPT_WEP
: WEP encryptionNET80211_CRYPT_TKIP
: TKIP, the original encryption method for WPANET80211_CRYPT_CCMP
: CCMP, a better encryption method added in WPA2NET80211_CRYPT_UNKNOWN
: anything else
Cryptosystems
Supporting a new crypto method, if one ever comes into use, is a
matter of updating net/80211/sec80211.c
to recognize it (if it
uses WPA handshaking, edit the rsn_cipher_map
array at the
beginning of that file) and adding a cryptosystem to implement it.
Similarly to security handshakers, cryptosystems are provided using a
linker table: define a struct net80211_crypto
tagged with
_
_net80211_crypto
and including some or all of the following
fields:
algorithm
: thenet80211_crypto_alg
enumeration value implementedpriv_len
: the size of private data to allocateinit()
: initialize cryptosystem with a specific key and RSCencrypt()
: make an encrypted copy of an I/O buffer containing a frame to transmitdecrypt()
: make a decrypted copy of an I/O buffer containing an encrypted frame that was received
The cryptosystem implementations are expected to be “pure”; that is,
they should not need to access any properties of the 802.11 device
that are not contained in the frame headers themselves. To enforce
this expectation, they are not provided with a net80211_device
pointer.
The implementation of sec80211_install
simply searches for a
cryptosystem implementing the requested algorithm, allocates storage
for it in dev→crypto
or dev→gcrypto
, and calls its
init()
method with the key and RSC it is given.
More 802.11 details
The 802.11 code is extensively documented using Doxygen comments, on both API and internal functions. To further understand what it's doing, I recommend the following sources:
- The official IEEE standard for 802.11-2007 (later standards may be available at GetIEEE 802.11). While very complex and incredibly acronym-laden, it explains things unambiguously and more-or-less clearly, and is invaluable for really understanding what's going over the wire.
- The Linux wireless code (in
net/mac80211/
and, to a lesser extent,net/wireless/
); while much more complicated than gPXE's implementation, it is an excellent, robust, and well-documented example of an 802.11 stack. - The source for the Linux daemon program
wpa_supplicant
, which handles the WPA Four-Way Handshake implemented by gPXE innet/80211/wpa.c
. - The IEEE standard for 802.1X. This defines some data structures and semantics that are used in the WPA 4-Way Handshake.
Hints for reading the 802.11 spec
The standard is divided into 19 “clauses” (roughly chapters) and 16 “annexes” (appendices). You certainly do not need to read all of them. The useful ones are
- Clause 3 (Definitions) and Clause 4 (Abbreviations and acronyms). Read these many times, and frequently refer to them when the main text gets impenetrable. The worst part of 802.11-2007 is the incredibly frequent use it makes of obscure acronyms; these are your guide to demystifying them.
- Clause 5 (General description). Read it once, briefly, but it's not worth spending much time on.
- Clause 7 (Frame formats) is exactly what it says: bit-level detail about all the types of frames you can use. The standard doesn't tell you what's commonly used; for instance, fragmentation and RTS/CTS are just about never used in practice, and you can get away without implementing QoS perfectly fine.
- Clause 8 (Security) contains all the necessary information for dealing with encrypted networks. 8.2.1 discusses WEP, 8.3 discusses what gPXE calls wireless cryptosystems for WPA, and 8.5-8.6 discuss what we call the security handshaking side of WPA. Note that the semantics given are all for RSN-style encrypted networks; before the standard for WPA was published, a different style was used, called vendor-style or (confusingly) WPA-style. The wpa_supplicant source code is the best reference I've found for the behavior of “old” WPA; I've tried to highlight the differences in gPXE.
- Clause 9 (MAC sublayer functional description) contains mostly things that are handled in hardware, but see 9.2 and 9.4-9.8.
- Clause 10 (Layer management) is basically a suggested API for providing the 802.11 services to software. There is no technical reason to implement it over any other, but other parts of the standard might refer to “submits MLME-ASSOCIATE.request” instead of “sends an association request frame”.
- Clause 11 (MLME) contains information about the association process. Only 11.1-11.3 are generally useful; the rest covers QoS and spectrum management issues that don't really need to be implemented.
- The rest of the spec deals with PHY-layer details.
- Annex H (RSNA reference implementation and test vectors) was useful in checking the security code.
If you run into something you can't figure out, email me (username oremanj located at the domain rwcr in the TLD .net) and I'll be happy to try and clarify.