Wireguard modified to support IPv6 SLAAC

I said in my previous post that modifying the wireguard kernel module, with the help of AI, was for another day. Well, it didn’t take long before I wanted to give it a go. The eBPF fix for SLAAC on wireguard worked, just fine for me, but really felt too hacky. So, I took the wireguard source and told a combination of Gemini (mostly) and at times a bit of GPT5-mini what I wanted to do and let it have a go. The result was pleasantly surprising.

Version 1 – this reused the existing AllowedIPs trie. It seemed the obvious way to do it, simply get in where the current peer IP isn’t recognised and insert it into the trie, and from then on it all just works. Except, it’s not possible to update the trie there, it had to be delegated to a worker, so it leaves a window where we allow packets though, but don’t yet know how to route them back. Managing the arrival of multiple new addresses for a peer at once gets complicated, it’s harder to keep track of a list of per peer learned IPs for chucking the oldest when we reach our limit, etc. And the worst bit, we need a per device lock every time we learn an IP, so I had to debug multiple different deadlocks before finally getting something to work. But it did, and it worked well.

Version 2 – I decided there must be a cleaner way to do things, so explaining my concerns to Gemini, we had another go. This time we went for a separate hash table of learned IPs. On the surface this seems more complex, but it’s not really, it actually simplifies a lot. All our learned IPs are /128, so we don’t need to find matching prefixes, just matching values. We can update immediately, no deferred worker thread, we don’t need to lock the device each time, there are cleaner ways to track a per-peer list, etc. It keeps the learned IPs separate, meaning a bit more update to the wg userspace tool, but not a big deal. It felt like a bit more code, but gave neater separation of new functionality and only one deadlock needing fixing in the whole second version.

I’ve been running it for a while now, and it seems pretty solid. As before, the introduction of auto learning undermines the incoming filter element of wireguard’s AllowedIPs design, but that’s really by definition of auto learning.

Source code on Github: https://github.com/raburton/wireguard-slaac

Leave a Reply