nRF24 sniffing
The wireless 2.4GHz Nordic Semiconductor nRF24L01+ chip (or nRF24 for short), which I'm experimenting with for wireless domotica applications, does however not support promiscuous mode, which in theory makes it impossible to capture network traffic between different nodes on a network.Some time ago I stumbled upon a blogpost by Travis Goodspeed, in which he explains how reducing the wireless network address length of an nRF24 (together with some other tricks) allows capturing traffic not directly addressed to this chip.
After reading his blog and some more google'ing around I started thinking of building a 'simple' network sniffer for nRF24 traffic. I faced following requirements & challenges:
- Based on commodity, cheap hardware
- Traffic capturing for network with known parameters (channel, baudrate, base address)
- Analysis using Wireshark network protocol analyzer
- Possibility to analyze protocols which use the nRF24 for transport in their network
Packet formats
A regular packet is sent on air by the nRF24 in the following format (supposedly; it's not in the datasheet):
Every message starts with a preamble, which the radio uses to identify incoming packets. The target node address follows and can be specified as 3 to 5 bytes (according to Travis Goodspeed a length of 2 is also accepted by the radio, though defined as invalid in the datasheet). Then comes the fixed-size payload, followed by an optional CRC to check the integrity of the message after reception. The CRC is calculated over the whole message, excluding the preamble and the CRC itself. The radio must be told upfront what the length of the payload will be, as a regular packet contains no indication of the length.
Regular nRF24 packet |
When a message received matches the receiver address of the radio and the CRC is found valid or disabled, the radio will store the payload in an internal FIFO for reception by the host (microcontroller or whatever connected to the nRF24 using SPI).
The nRF24L01+ modules support a mode called 'Enhanced Shockburst' which has a number of advantages over regular usage (incomplete):
- Payload lengths can be set dynamically and are part of the message
- The receiving node can automatically send an acknowledge to the sender to indicate that the message has been received correctly. The sender will automatically retry transmission a number of times when the acknowledge is not received within a configurable timeout (Nordic calls this "Automatic packet transaction handling")
Enhanced Shockburst nRF24 packet |
As you can see a 9 bit packet control field has been added which stores the payload length (in bytes), a packet identifier (PID) to detect retransmissions and a flag to suppress sending acknowledge packets on a per-packet basis. The CRC is no longer optional.
As with the regular packets, a message received must match the receiver address of the radio and the CRC must be valid to have the payload stored in the internal FIFO, otherwise the message will be discarded.
Many (all?) wireless sensor applications based on the nRF24L01+ I've seen so far run the radio in Enhanced Shockburst mode.
Promiscuous reception
When building a wireless network of many nodes you choose a single RF channel which all nodes will operate on. Furthermore each node shall have a unique address within the network (unless you're only broadcasting). Each node can have an arbitrary address, but normally you choose a base address which is identical for all nodes, and a node address which is unique for each node. Combined these form the address:
This example addressing scheme (4+1) is used by the MySensors library and allows 255 unique nodes to be addressed (node 255 is used for broadcast addressing).
Should the nRF24 support promiscuous addressing, then the base address would have to be configured in the chip and it should be instructed that the node addressing consists of 1 byte. The nRF24 would then start listening for messages. All messages with matching base address and valid CRC would then be stored...
But the nRF24 does not support promiscuous listening. With some trickery we can however instruct it to capture all messages for a certain base address, including the ones with invalid CRC's !
The following configuration of the nRF24 sniffing radio is required:
Address consisting of 4 byte base address and single byte node address |
Should the nRF24 support promiscuous addressing, then the base address would have to be configured in the chip and it should be instructed that the node addressing consists of 1 byte. The nRF24 would then start listening for messages. All messages with matching base address and valid CRC would then be stored...
But the nRF24 does not support promiscuous listening. With some trickery we can however instruct it to capture all messages for a certain base address, including the ones with invalid CRC's !
The following configuration of the nRF24 sniffing radio is required:
- Set the nRF24 receiver address to just the base address
- Disable Enhanced Shockburst
- Disable CRC checking
- Configure a fixed payload size
The first 3 items will cause the nRF24 to capture anything that matches the base address of the network we're sniffing. As Enhanced Shockburst is disabled, the radio will not determine the payload size from the message anymore and we just have to store a fixed amount of data starting from the Node address. We just fake the message received is a regular packet, while in fact it is an Enhanced Shockburst packet!
CRC checking has to be disabled of course, as the nRF24 doesn't know the payload length anymore and CRC calculation would fail, rejecting all messages.
The process is illustrated by the image below (again, taking the 4+1 addressing scheme as an example):
The payload received will start with the node address the packet was transmitted to, so effectively we've created a promiscuous listening nRF24!
As the nRF24 will no longer dissect the packet for us, we'll have to do it ourselves (actually let Wireshark do it for us). The original length of the payload should be extracted (the 6 bit payload length), the full address recreated and CRC calculated over the data received. When the CRC is invalid the message can be marked, which gives us a nice indication of the link quality. As the nRF24 header is not a multiple of 8 bits, all payload & CRC bytes will be bit-shifted directly adjacent to the packet control field. This is inconvenient when inspecting the raw bytes in a packet, but Wireshark will solve this nicely for us.
CRC checking has to be disabled of course, as the nRF24 doesn't know the payload length anymore and CRC calculation would fail, rejecting all messages.
The process is illustrated by the image below (again, taking the 4+1 addressing scheme as an example):
Capture Enhanced Shockburst packet as regular packet |
As the nRF24 will no longer dissect the packet for us, we'll have to do it ourselves (actually let Wireshark do it for us). The original length of the payload should be extracted (the 6 bit payload length), the full address recreated and CRC calculated over the data received. When the CRC is invalid the message can be marked, which gives us a nice indication of the link quality. As the nRF24 header is not a multiple of 8 bits, all payload & CRC bytes will be bit-shifted directly adjacent to the packet control field. This is inconvenient when inspecting the raw bytes in a packet, but Wireshark will solve this nicely for us.
Limitations
There are however some limitations to this method which you should be aware of:- The maximum length of a payload is limited by the nRF24 to 32 bytes. As we 'shift' the reception of the payload towards the start of the packet and also want to include the CRC in the payload, the effective size of payloads captured will go down by a number of bytes. The amount depends on the number of bytes in the node address, the fixed packet control field and the length of the CRC.
- When fixed payload size of the sniffer is set to a value (much) larger than the actual payload size in the packet, then subsequent packets might be missed. When the nRF24 is still capturing the fixed size payload and a new packet arrives, this will not be detected. A good example for this are the auto-acknowledge packets which can be enabled in Enhanced Shockburst mode. After the target node receives the packet (and it will know the real size of the payload a lot faster than the sniffer will) it switches its radio from receiving to transmitting mode in only 130us. Then it sends out the acknowledge packet. Reception of 32 bytes at 1Mb/s takes 32*8us = 256us, so acknowledge of a short message will definately be missed. For acknowledge packets this isn't much of a problem as the transmitter will retry to send the identical message when no acknowledge was received and we will know it missed the acknowldge.
- This method works well for Enhanced Shockburst packages, as they have the payload size embedded in the message. For regular messages the length will have to be known or can be determined by testing different lengths until the CRC matches. With CRC disabled the payload size can only be guessed.
- The addressing scheme must use a base address in the high address byte(s), and node address in the low byte(s), as explained.
What's next
In the next parts of this series I will dive deeper in the hardware and software required to create the sniffer and give some nice usage examples. Furthermore a Wireshark dissector for the MySensors protocol will be presented.