802.11p V2X hunting

Posted on Oct 15, 2019

Autonomous vehicles are becoming closer to reality, and the technologies developed to support them have already started hitting the market. I set out to see if I could find any real-world deployments of these systems.

What’s out there

To give a bit of context, various standards have been developed (and are under development) to help support technologies behind autonomous vehicles. For example: collision avoidance systems, cooperative adaptive cruise control, and basically any scenario where a car needs to talk to another car (V2V) or a car needs to talk to the road (V2X).

One standard, known as ITS-G5 defines a protocol that enables such communication. ITS-G5 is built on top of 802.11p, a LAN protocol very similar to the more commonly known 802.11a used in home and office networks.

Interoperability and compatibility were key considerations when ITS-G5 was developed. It was important to ensure that the standard was easily adopted by the industry, and that manufactures didn’t go off and develop their own proprietary protocols. Additionally, it had to address the physical considerations of operating in a fast-moving outdoor environment.

802.11p, and therefore ITS-G5, utilize a special wireless operation mode referred to as OCB, or Outside the Context of a BSS. Network layer security was intentionally removed from OCB to reduce overhead and ensure interoperability. All stations are locked to a pre-determined frequency and simply broadcast packets without having to explicitly perform any type of network association. Rather, ITS-G5 achieves security through a series of data structures involving special headers and public key infrastructure on the application layer.

This means that it will always be possible to capture and/or inject ITS-G5 network layer traffic, though depending on the specific implementation application traffic may or may not be encrypted.


There’s a couple different directions you could take here. Since 2014, various Linux kernel patches have been released that modify the wireless stack and Atheros 802.11n driver (ath9k) to support 802.11p. Several off-the-shelf WiFi cards have been reported to work with these patches. Companies have used these or similar kernel modifications to build production 802.11p devices, and this is the route I chose.

You could also take the software defined radio route. There’s a project maintained by Bastian Bloessl that implements a 802.11 a/g/p transceiver in GNU Radio. In retrospect this may have been a cleaner solution, but it requires an Ettus Research USRP radio which I found hard to justify for this project.

I followed advice from the GRCDEV repository and purchased a PC Engines APU2 board with the Compex WLE200NX miniPCI wireless card. Several others have used this configuration as well, so I figured it was a reasonable choice.

There’s basically two main requirements that need to be fulfilled here. The first is that the card needs to use the ath9k driver, because as far as I can tell that’s the only driver that’s been publicly modified to support 802.11p and OCB mode. Though technically there shouldn’t be any reason why other drivers couldn’t be modified as well, ath10k for example. Second is that the card needs to support operating on frequencies outside of the standard WiFi bands. Standard 5 GHz WiFi channels end towards the upper end of 5.8 GHz, while the channels allocated for ITS-G5 are closer to 5.9 GHz. Additionally, most (if not all) ath9k cards are Mini-PCIe format, so you need some hardware with a Mini-PCIe slot, hence the APU2 board. A laptop with a Mini-PCIe slot should also work in place of the APU2 board.


Things get a bit tricky here. There’s no good single patch to use. The Czech Technical University published a GitHub repository with some patches back in 2014, and while it remains a good reference, they are no longer compatible with recent versions of the kernel.

To add to the confusion, since 2014, some but not all of the changes to support 802.11p have been merged into the mainstream Linux kernel. This means if you want to implement 802.11p in a modern kernel you’re stuck sifting through the code to determine what has been implemented and what hasn’t.

To get 802.11p working in Linux there’s a few modifications that need to be made:

  • The wireless stack needs to support OCB mode
  • The wireless driver needs to support OCB mode
  • The wireless driver needs to be modified to allow ITS-G5 channels
  • The Linux regulatory database needs to be modified to allow ITS-G5 channels

It looks like the mainstream kernel has made an effort to support OCB mode both in ath9k and the wireless stack, but for regulatory / compliance reasons if you want to use ITS-G5 channels you need to compile your own kernel and update the regulatory database yourself.

Unfortunately, I didn’t have any luck getting OCB mode to work on unmodified versions of Debian 10 stable nor Ubuntu 18.04.3 LTS. Even on standard WiFi channels. Whenever I attempted to enter OCB mode the kernel would simply crash. I assume at some point there was a breaking change made to the kernel, and there aren’t enough people using the feature to have noticed or bother trying to fix it (myself included).

I’ve made a small collection of various patches I found, and have included them at the end of the article for reference.

In the end I landed on using OpenC2X, an “Open Source Experimental and Prototyping Platform Supporting ETSI ITS-G5” by Paderborn University and the Heinz Nixdorf Institute in Germany. OpenC2X is a bit old and I’ve found little in the way of documentation, but it includes all the changes required to use 802.11p, and it didn’t require debugging kernel drivers.

Compiling OpenC2X

OpenC2X has an embedded branch, that’s built on top of LEDE/OpenWRT and already includes all the kernel modifications to use 802.11p. After compiling OpenC2X, you can follow the standard procedure for installing OpenWRT on the APU2.

You’ll need a Linux machine to build OpenC2X, and preferably something with a bit of power. (ie. not the APU2) Build times on a modern PC are in the range of 30 minutes to an hour. I used Ubuntu 18.04.3 LTS running in a virtual machine. To start, install some dependencies:

sudo apt install git build-essential libncurses5-dev zlib1g-dev unzip python

Clone the OpenC2X embedded repository, update the OpenWRT feeds, and create a kernel configuration file.

git clone https://github.com/florianklingler/OpenC2X-embedded.git
cd OpenC2X-embedded
./scripts/feeds update -a
./scripts/feeds install -a
./create_config.sh x86_64
make defconfig

Running make menuconfig should bring up the kernel configuration tool.

There’s a few extra tools we want to include in our custom kernel. Browse to the following menu locations, and make a selection by pressing Y. When finished select Save to update the configuration file.

Network > tcpdump
Network > firewall > iptables > iptables-mod-tee
Network > Time Synchronization > chrony
Utilities > strace
Utilities > grep
Utilities > Shells > bash
Utilities > Editors > nano

There’s a bug in the version of e2fsprogs that’s shipped with OpenWRT, where it won’t build on systems with glibc 2.27 or newer. See this pull request. A quick and dirty fix is to rename all instances of the copy_file_range system call to copy_file_chunk in ./build_dir/host/e2fsprogs-1.43.3/misc/create_inode.c.

Now you can build OpenC2X, where X is the number of jobs to run in parallel. A good rule of thumb is to run a number of jobs equal to the number of cores your computer has +1. For example, if you have a processor with 8 cores you would run make -j9 V=s.

make download
make -jX V=s

Your computer will enter the matrix for a while, and when it gets back you should have flashable OpenC2X images in ./bin/targets/x86/64/.

If the build fails, run make dirclean and make -j1 V=s, which will launch the build process as a single job. It will run slower, but when it fails the output will be more verbose to help you debug.

Flashing OpenC2X on the APU2

A quick and easy way to flash firmware to the APU2 is with PC Engines Tiny Core Linux. Follow their instructions for creating a bootable USB drive, and copy over lede-x86-64-combined-ext4.img.gz. Note, the disk needs to be FAT32, so if your USB drive is larger than 32 GB you’ll need to shrink the partition.

Connect the APU2 to your computer with the serial adapter, instructions here. Then run the following commands to copy over the firmware:

gunzip lede-x86-64-combined-ext4.img.gz
dd if=/media/TINYCORE/lede-x86-64-combined-ext4.img of=/dev/sda bs=4M

Reboot, and you should be automatically logged in. Follow the recommendation and set the root password.

Capturing packets

You can connect to OpenC2X with SSH. By default, the network port closest to the serial port is designated as a LAN interface with DHCP. You’ll get an IP in OpenC2x is on

Set the regulatory region to Germany, enable OCB mode, bring up the interface, and join a channel. Germany is the only country that’s been modified in the regulatory database to use ITS-G5 channels. If you like you can modify the database yourself before compiling the kernel. Take a look at ./package/kernel/mac80211/files/regdb.txt. (file format definition)

iw reg set DE
iw dev wlan0 set type ocb
ip link set wlan0 up
iw dev wlan0 ocb join 5900 10MHZ

Now we’re part of an OCB network! If you want to perform packet sniffing, we can bring up a new virtual interface in monitor mode.

iw dev wlan0 interface add wlan1 type monitor
ifconfig wlan1 up

# Enable promiscuous mode (not sure if this is necessary on both interfaces, shouldn't hurt either way)
ifconfig wlan0 promisc
ifconfig wlan1 promisc

In my case I didn’t know what channel or bandwidth configuration I would find traffic on, so I used a script to cycle through different settings while I captured traffic with tcpdump.

First, you can create a file called freq.txt that lists the channels you want to cycle through.

root@LEDE:~# cat freq.txt

The script below will read freq.txt and cycle through the different frequencies and bandwidth configurations.


iw dev wlan0 ocb leave
for freq in $(cat freq.txt); do
        echo "$freq 10MHZ"
        iw dev wlan0 ocb join $freq 10MHZ
        sleep 1
        iw dev wlan0 ocb leave
        echo "$freq 5MHZ"
        iw dev wlan0 ocb join $freq 5MHZ
        sleep 1
        iw dev wlan0 ocb leave

With the script running in the background, you can use tcpdump to collect packets. I ended up running two separate instances: one to dump data to a pcap, and one to output to the console so I could see if I was collecting traffic in real-time.

# output to pcap for later analysis in Wireshark
tcpdump -s 0 -i wlan1 -w foo.pcap

# output to console
tcpdump -i wlan1

For anybody wondering, this configuration is passive and simply monitors for traffic. It does not transmit or interfere with other devices in any way. That’s not to say that the device isn’t capable of transmitting, so caution is advised.

802.11p wardriving

With everything set up, I drove around Oslo to see if I could find any 802.11p traffic. I was hoping that I would see some traffic from the fancy new and controversial toll stations, but it looks like they are still using an older protocol more closely related to RFID.

Though after driving around for a while, a single packet showed up in tcpdump.

20:18:01.206949 1034504430us tsft 6.0 Mb/s 5900 MHz 11a/10Mhz -87dBm signal -92dBm signal antenna 0 -88dBm signal antenna 1 GeoNet src:04:e5:48:dc:a6:e8 (oui Unknown); v:1 NH:1-BTP-A HT:0-0-Any HopLim:0 Payload:8272 GN_ADDR:00:1e:01:00:3c:00:04:e5 lat:-479120736 lon:599278530; BTP Dst:0 Src:0; ItsPduHeader v:7 t:209-unknown (209)

At first didn’t really know what I was looking at, but I saw GPS coordinates and had a source MAC address so I did some research. The MAC address points to a device produced by Cohda Wireless Pty Ltd, and the GPS coordinates weren’t too far away from where I detected the packet.

A bit of googling turned up a press release from Cohda Wireless, that covered their V2X-Locate product and a proof of concept they conducted with the Norwegian company Aventi. As it turns out, Aventi’s headquarters was only a few hundred meters away from where I detected the packet. What I’m guessing I found was a test installation of V2X-Locate.

I went back later to get some more sample data. Feel free to download the PCAP and take a look yourself.

I didn’t find any production real-world deployments, but I did find actual ITS-G5/802.11p traffic in the wild. So I’ll count that as a success :)

I did drive through the Bjørnegård tunnel in Bærum where Aventi did their proof of concept with Cohda Wireless, but I didn’t find anything. I guess the equipment has since been disabled or removed.

ITS-G5 security considerations

There are strong parallels here between ITS-G5 and connecting to a wireless network at Starbucks. As I discussed earlier, network layer security has been intentionally removed from OCB, the wireless mode used by 802.11p and ITS-G5. This is a near identical setup to using an open wireless network on your laptop. Technically this doesn’t have to be an issue if security is handled properly at the application layer. In this instance, ITS-G5. But that’s a big IF.

If you’re on the web over an open wireless network hopefully the websites you’re browsing use HTTPS, but maybe they don’t. Though even if they do there are still risks. Even my mom knows to look for the little green lock in the URL bar.

The European Telecommunications Standards Institute (ETSI) has defined how to implement security in ITS-G5 (ETSI TS 103 097). But they left the decision to use it up to whoever implements the standard. The data I captured is more than likely from a test system, so it’s hard to make any definitive statements here. But from what I can tell V2X-Locate does not implement application layer security. Meaning, their packets can be spoofed/forged.

Whether or not you can consider this an issue somewhat depends on how the system is used. For example, if V2X-Locate is never used in sensitive applications, then spoofing location data might not be a problem. Though I suspect this and similar solutions will be considered by Norwegian Public Roads Administration for installation across Norway to support the rollout of a per-kilometer road fee. This proposed system would replace traditional road tolls, and drivers would be charged based on where and when they drive. To achieve this, cars will need to be fitted with a tracking device that monitors location (oh god). GPS alone probably won’t be accurate enough due to the vast network of tunnels in Norway, hence the requirement for ITS-G5 based location services.

My personal opinion is that ETSI made a mistake by not mandating security in ITS-G5. Yes, the option is there, but as we learned before with HTTPS, that doesn’t mean it will be used or implemented correctly. If I were to make a prediction, whenever self driving cars finally get to market, or ITS-G5 is used in safety critical applications, large auto manufactures will probably implement security correctly. They have the resources to do so and a brand to protect.

I start to get concerned when I think about the relatively smaller installations done by cities and governments who work with smaller companies that build traffic management solutions, such as tolling. While nobody wants bad press, the government will always receive funding, and smaller companies with lesser known brands simply won’t be impacted by a breach as much as say Toyota or Tesla.

I would like to see a world where these systems are subject to mandated thorough security testing, by law.


Kernel patches