-
Notifications
You must be signed in to change notification settings - Fork 28
802.15.4
Some advice and scripts on how to get 802.15.4 working and solving RFCTF challenges.
Standard for low-power, low data rate wireless communication between small devices and forms the basis wireless personal area networks (WPANs) with low transmitter power, small MTU, low power consumption and low cost. Because of these facts 802.15.4 is ideally used in industrial control and monitoring, sensor networks, intelligent agriculture, security systems and smart grids. 802.15.4 is a MAC and PHY layer protocol (OSI layers 1 and 2) which is used by many types of higher-level protocols (layer 3) like Zigbee, Z-Wave and 6Lowpan.
Assumes a host platform of RPi3 B running rasbian stretch in userspace with a 4.9 kernel. The openlabs ATMEL 802.15.4 radio is the radio transceiver being used (https://openlabs.co/OSHW/Raspberry-Pi-802.15.4-radio). The following was also tested on a 5.2 kernel but the userspace didn't seem to work. YMMV with any other kernel version than 4.9.
To install a 4.9 kernel you can easily do the following
rpi-update 936a8dc3a605c20058fbb23672d6b47bca77b0d5
Or build the kernel yourself. For this you'll need to make sure some modules are already built with your kernel. Whether we enable them as modules or as built-ins doesn't much matter, but we'll have to be sure to copy the modules over to the RPi later if we choose to build them as modules. At the very least the following will need to be built.
CONFIG_IEEE802154
CONFIG_IEEE802154_6LOWPAN
CONFIG_MAC802154
CONFIG_IEEE802154_AT86RF230
CONFIG_IEEE802154_FAKELB
Follow the instructions at https://www.raspberrypi.org/documentation/linux/kernel/building.md in order to build the proper kernel. Remember to install the modules as well if you built them that way.
The AT86RF233 is an SPI device, so it can't be auto-enumerated and needs to go into the device tree so the kernel is aware of it at boottime; essentially telling the kernel which driver to use and what the pinout is. Because of this we'll need to install the appropriate overlay. This can be handled in one of two ways.
For the particular kernel in use (5.0.15) there already exists an overlay for the radio transceiver (should be at86rf233-overlay.dtb or at86rf233.dtbo). You'll need to just copy this to /boot/overlays/.
From here just plug in the radio transceiver into pins 15-26 where the hat points inwards towards the pi. You should have 7 pins to the left and to right (centered) among all the gpio pins. From here just enable the transceiver in the /boot/config.txt by adding/uncommenting the following line.
dtoverlay=at86rf233
Now reboot the pi!
If the pi has no overlay for this transceiver (at86rf233-overlay.dtb or at86rf233.dtbo) by default you'll need to build and install one yourself. First install the device tree compiler.
apt-get install device-tree-compiler
Then create the overlay file at86rf233-overlay.dts with the following contents.
/dts-v1/;
/plugin/;
/* Overlay for Atmel AT86RF233 IEEE 802.15.4 WPAN transceiver on spi0.0 */
/ {
compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709";
fragment@0 {
target = <&spi0>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
lowpan0: at86rf233@0 {
compatible = "atmel,at86rf233";
reg = <0>;
interrupt-parent = <&gpio>;
interrupts = <23 4>; /* active high */
reset-gpio = <&gpio 24 1>;
sleep-gpio = <&gpio 25 1>;
spi-max-frequency = <3000000>;
xtal-trim = /bits/ 8 <0xf>;
};
};
};
fragment@1 {
target = <&spidev0>;
__overlay__ {
status = "disabled";
};
};
fragment@2 {
target = <&gpio>;
__overlay__ {
lowpan0_pins: lowpan0_pins {
brcm,pins = <23 24 25>;
brcm,function = <0 1 1>; /* in out out */
};
};
};
__overrides__ {
interrupt = <&lowpan0>, "interrupts:0",
<&lowpan0_pins>, "brcm,pins:0";
reset = <&lowpan0>, "reset-gpio:4",
<&lowpan0_pins>, "brcm,pins:4";
sleep = <&lowpan0>, "sleep-gpio:4",
<&lowpan0_pins>, "brcm,pins:8";
speed = <&lowpan0>, "spi-max-frequency:0";
trim = <&lowpan0>, "xtal-trim.0";
};
};
Compile and install:
dtc -@ -O dtb -o at86rf233.dtbo at86rf233-overlay.dts
cp at86rf233.dtbo /boot/overlays/.
From here just plug in the radio transceiver into pins 15-26 where the hat points inwards towards the pi. You should have 7 pins to the left and to right (centered) among all the gpio pins. From here just enable the transceiver in the /boot/config.txt by adding/uncommenting the following line.
dtoverlay=at86rf233
Now reboot the pi!
To test that the 802.15.4 network stack is in place you can easily look for the loaded modules if you built them that way.
root@raspberrypi:/home/pi# lsmod | grep 802
mac802154 73728 1 at86rf230
ieee802154 81920 1 mac802154
crc_ccitt 16384 1 mac802154
cfg80211 610304 1 brcmfmac
Or you can load up some of the userspace tools and that will provide feedback that the 802.15.4 stack is in place.
At the very least install both wpan-tools and lowpan-tools. If needed a recent version of wpan-tools is available to download and build at https://github.com/linux-wpan/wpan-tools. The lowpan-tools can be found at https://github.com/linux-wpan/lowpan-tools.git (horribly outdated source code).
apt-get install lowpan-tools wpan-tools
List current IEEE 802.15.4 wpan interfaces.
root@raspberrypi:/home/pi# iz list
wpan0 (3)
link: IEEE 802.15.4 MAC interface
phy phy0
hw 2e:6c:d2:a0:52:d3:22:74 pan 0xffff short 0xffff
Access the nl802154 netlink inteface and checking the network connectivity and capabilities of the attached 802.15.4 interfaces.
root@raspberrypi:/home/pi# iwpan list
wpan_phy phy0
supported channels:
page 0: 11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
current_page: 0
current_channel: 13, 2415 MHz
cca_mode: (1) Energy above threshold
cca_ed_level: -80
tx_power: 4
capabilities:
iftypes: node,monitor
channels:
page 0:
[11] 2405 MHz, [12] 2410 MHz, [13] 2415 MHz,
[14] 2420 MHz, [15] 2425 MHz, [16] 2430 MHz,
[17] 2435 MHz, [18] 2440 MHz, [19] 2445 MHz,
[20] 2450 MHz, [21] 2455 MHz, [22] 2460 MHz,
[23] 2465 MHz, [24] 2470 MHz, [25] 2475 MHz,
[26] 2480 MHz
tx_powers:
4 dBm, 3.7 dBm, 3.4 dBm, 3 dBm, 2.5 dBm, 2 dBm,
1 dBm, 0 dBm, -1 dBm, -2 dBm, -3 dBm, -4 dBm,
-6 dBm, -8 dBm, -12 dBm, -17 dBm,
cca_ed_levels:
-94 dBm, -92 dBm, -90 dBm, -88 dBm, -86 dBm, -84 dBm,
-82 dBm, -80 dBm, -78 dBm, -76 dBm, -74 dBm, -72 dBm,
-70 dBm, -68 dBm, -66 dBm, -64 dBm,
cca_modes:
(1) Energy above threshold
(2) Carrier sense only
(3, cca_opt: 0) Carrier sense with energy above threshold (logical operator is 'and')
(3, cca_opt: 1) Carrier sense with energy above threshold (logical operator is 'or')
min_be: 0,1,2,3,4,5,6,7,8
max_be: 3,4,5,6,7,8
csma_backoffs: 0,1,2,3,4,5
frame_retries: 0,1,2,3,4,5,6,7
lbt: false
If the above userspace tools work then the 802.15.4 stack in the kernel is in place. From here be sure to setup the second pi. You did buy two RPis and two radios, right?
Pinging at the IEEE 802.15.4 level is a good test to make sure that the hats are working correctly and you can communicate. Make sure you can get this working before moving on.
Server setup on one of the pis.
ip link set wpan0 down
iwpan phy0 set channel 0 15
iwpan dev wpan0 set pan_id 0x1111
iwpan dev wpan0 set short_addr 0x0001
ip link set wpan0 up
$ wpan-ping -d 0x0002
Client setup on the other pi.
ip link set wpan0 down
iwpan phy0 set channel 0 15
iwpan dev wpan0 set pan_id 0x1111
iwpan dev wpan0 set short_addr 0x0002
ip link set wpan0 up
$ wpan-ping -a 0x0001 -s 5 -c5
PING 0x0001 (PAN ID 0x1111) 5 data bytes
5 bytes from 0x0001 seq=0 time=4.9 ms
5 bytes from 0x0001 seq=1 time=4.8 ms
5 bytes from 0x0001 seq=2 time=5.1 ms
5 bytes from 0x0001 seq=3 time=5.8 ms
5 bytes from 0x0001 seq=4 time=4.7 ms
--- 0x0001 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss
rtt min/avg/max = 4.695/5.075/5.818 ms
A chat session can be started on a net PANid, using addr sourceAddr talking with destAddr. This is included with the iz toolset (lowpan-tools) which can be used to chat directly over the 802.15.4 interface. On one pi assign the pan id the source and target address.
$ izchat 0x1111 0x0002 0x0001
help
> hello
On the other pi
$ izchat 0x1111 0x0001 0x0002
> help
hello
The iz tool makes it very easy to add a monitor interface to a 802.15.4 physical interface. By doing this promiscuous mode just works; this includes packets complete with original FCS and packets that aren't valid 802.15.4. First find the physical interface exposed by the radio.
root@raspberrypi:/home/pi# iz listphy
phy0 IEEE 802.15.4 PHY object
page: 0 channel: 13
channels on page 0: 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
You'll want to delete all wpan interfaces which sit on top of the wpan phy. You can get a list of all currently running phy interfaces with iwpan and then delete each one using iwpan. This is done because when sniffing we'll need to change the channel of the phy which will affect all wpan interfaces attached. So only having a single wpan interface (monitor) will help in this respect.
iwpan dev
iwpan dev $IFNAME del
Now activate a mon0 interface on the displayed physical interface
iz monitor phy0 mon0
From here you can tell that the mon0 interface is in monitor mode by using the iwpan tool
root@raspberrypi:/sys/class/net/mon0# iwpan dev
phy#0
Interface mon0
ifindex 5
wpan_dev 0x2
extended_addr 0x0000000000000000
short_addr 0xffff
pan_id 0xffff
type monitor
max_frame_retries 3
min_be 3
max_be 5
max_csma_backoffs 4
lbt 0
ackreq_default 0
Change the channel to the channel you care to sniff.
iwpan phy phy0 set channel 0 25
Check that the channel is correct
root@raspberrypi:/sys/class/net/mon0# iz listphy
phy0 IEEE 802.15.4 PHY object
page: 0 channel: 25
channels on page 0: 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Then bring the interface up
ifconfig mon0 up
Wireshark will work with an 802.15.4 interface out of the box. From here we're gonna sniff packets on the RPi with tcpdump and send them over ethernet using netcat to Wireshark running on another computer. On thr other computer start wireshark listening on the port.
nc -vlp 9000 | wireshark -ki -
Then on the pi start tcpdump sending over netcat. Be sure to set the correct ip of the laptop
tcpdump -i mon0 -Uw - | nc <laptop ip> 9000
To switch channels (monitor) we can just run the set channel command in a loop.
# Background tcpdump so we can switch the channel
Ctrl-Z && bg
# Switch the channel every 30 seconds
while true; do for i in $(seq 11 26); do iwpan phy phy0 set channel 0 $i; echo $i; sleep 30; done; done
With wireshark you should be able to see the chat data and the ping data. There does seem to be a lot of malformed packets in the capture and not sure if maybe this is a result of no antenna or what...
On of the pis
echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
ip link set lowpan0 down
ip link set wpan0 down
iwpan dev wpan0 set pan_id 0xcafe
iwpan phy0 set channel 0 26
ip link add link wpan0 name lowpan0 type lowpan
ip link set wpan0 up
ip link set lowpan0 up
ip addr add 2001:db8::1/64 dev lowpan0
On the other pi
echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
ip link set lowpan0 down
ip link set wpan0 down
iwpan dev wpan0 set pan_id 0xcafe
iwpan phy0 set channel 0 26
ip link add link wpan0 name lowpan0 type lowpan
ip link set wpan0 up
ip link set lowpan0 up
ip addr add 2001:db8::2/64 dev lowpan0
Ping over IPv6
$ ping6 -I lowpan0 <full ipv6 address if the other pi>
PING fe80::f86f:2842:b16f:20ae(fe80::f86f:2842:b16f:20ae) from fe80::c8fe:ff:fe00:2%lowpan0 lowpan0: 56 data bytes
64 bytes from fe80::f86f:2842:b16f:20ae%lowpan0: icmp_seq=1 ttl=64 time=12.0 ms
64 bytes from fe80::f86f:2842:b16f:20ae%lowpan0: icmp_seq=2 ttl=64 time=14.3 ms
64 bytes from fe80::f86f:2842:b16f:20ae%lowpan0: icmp_seq=3 ttl=64 time=12.9 ms
^C
--- fe80::f86f:2842:b16f:20ae ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 12.056/13.119/14.308/0.933 ms
If you got the 6lowpan ping to work then you can easily ssh from one pi to the other over 6lowpan.
ssh pi@fe80::f86f:2842:b16f:20ae%lowpan0
Included is some python code allowing a 6lowpan server and client to exchange temperature data; assuming you have both 6lowpan interfaces already up and running according to the above instructions.
6lowpan_client.py
#!/usr/bin/env python3
# Connect to the server and grab its temp
import socket
import time
# the other RPi
ADDR = 'fe80::f04a:bde1:f1da:32e7'
PORT = 2016
scope_id = socket.if_nametoindex('lowpan0')
while True:
s6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
s6.connect((ADDR, PORT, 0, scope_id))
data = s6.recv(1024)
print(data.decode('utf-8'), end='')
# get it again after 10 seconds
time.sleep(10)
6lowpan_server.py
#!/usr/bin/env python3
# Wait for clients to connect and report back this pis temp
import socket
from subprocess import PIPE, Popen
HOST = ''
PORT = 2016
def get_cpu_temperature():
process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
output, _error = process.communicate()
return output
s6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
s6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
scope_id = socket.if_nametoindex('lowpan0')
s6.bind((HOST, PORT, 0, scope_id))
s6.listen(1)
while True:
conn, addr = s6.accept()
conn.send(b'server: ' + get_cpu_temperature())
conn.close()
# Server
$ ./6lowpan_server.py
# Client
$ ./6lowpan_client.py
server: temp=47.8'C
server: temp=47.2'C
4 types of packets
- beacon: Sent by Coordinator to set up the Superframe structure. Transmitted by coordinators periodically. Offers synchronization to the network and to identify the network and its structure.
- ack: Provide confirmation of reception
- data: transfers application data.
- command: Associate, Disassociate,Beacon request, GTS request
beacon-enabled versus nonbeacon-enabled network: passive vs active scanning
pages and channels: page 0: 0 at 868MHz, 1-10 in 915mhz, 11-26 in 2.4GHz, 27-31 reserved page 1: -10 page 2: 0-10 page 3: 0-13 page 4: 0-15 page 5: 0-7 page 6: 0-10
- frame type: beacon, ack, data, command
- Security Enabled=1 and Frame Version>0 (frame versions 2006 and later), an Auxiliary Security Header field will be present in the MHR and will need to be further parsed by the packet processor if this is a MAC Command frame (see 4.1.3 Source Address Matchingsection below). There is no other use of Security Enabled by the packet processor
- Frame Pending: ignored
- Ack Request: Will be stored internally by the sequence manager. An auto-TxAck frame will follow the incoming receive frame if necessary conditions are met
- PAN ID Conpression:
- Sequence Number: Directly following the Frame Control Field is the Sequence Number field. The Sequence Number field of the MHR is captured by the packet processor. If an auto-TxAck frame follows the incoming receive frame, the captured Sequence Number will be copied to the transmitted Acknowledge packet, and the captured Frame Version will be inserted into the Frame Control Field of the transmitted packet.
.... .... .... .001 = Frame Type: Data (0x1)
.... .... .... 0... = Security Enabled: False
.... .... ...0 .... = Frame Pending: False
.... .... ..1. .... = Acknowledge Request: True
.... .... .1.. .... = PAN ID Compression: True
.... .... 0... .... = Reserved: False
.... ...0 .... .... = Sequence Number Suppression: False
.... ..0. .... .... = Information Elements Present: False
.... 10.. .... .... = Destination Addressing Mode: Short/16-bit (0x2)
..00 .... .... .... = Frame Version: IEEE Std 802.15.4-2003 (0)
10.. .... .... .... = Source Addressing Mode: Short/16-bit (0x2)
Sequence Number: 93
Destination PAN: 0x1111
Destination: 0x0001
Source: 0x0002
FCS: 0xbdf8 (Incorrect, expected FCS=0xae25)
[Expert Info (Warning/Checksum): Bad FCS]
In wireshark udp setup use rftap (lower case) Listen udp:port must match GNU rfTAP. From wireshark startview (config dialog): Wireshark - Interface Options:UDP Listener UDP remore capture:udpdump Listen Port should match what is in GmuRadio:Socket PDU Port number, such as 52006 Payload Type: rftap
In GnuRadio/RFtap Encapulation use: Type=Custom dissector, Data Link Type = -1, Disector=wpan NOTE lower case wpan is the id of IEEE 802.15.4 N.B. this is different from the RFtap example as Data Link Type 195 did not work for me