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 like iwlist to use them as well. Once a good AP has been found, the NET80211_PROBED bit is set in dev→state, and the dev→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, the NET80211_AUTHENTICATED bit is sert in dev→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 in dev→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), the NET80211_CRYPTO_SYNCED bit is set in dev→state and the wrapping net_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 of net80211_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() in net/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 sets dev→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, and start() and step() are NULL.
  • 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 the netX/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 security
  • NET80211_CRYPT_WEP: WEP encryption
  • NET80211_CRYPT_TKIP: TKIP, the original encryption method for WPA
  • NET80211_CRYPT_CCMP: CCMP, a better encryption method added in WPA2
  • NET80211_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: the net80211_crypto_alg enumeration value implemented
  • priv_len: the size of private data to allocate
  • init(): initialize cryptosystem with a specific key and RSC
  • encrypt(): make an encrypted copy of an I/O buffer containing a frame to transmit
  • decrypt(): 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 in net/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.