- cross-posted to:
- [email protected]
- cross-posted to:
- [email protected]
Hi all home network administrators :) Haven’t posted anything here since June, when I told you about Gatekeeper 1.1.0. Back then it was a pretty bare-bones (and maybe slightly buggy) DNS + DHCP server with a web UI with a list of LAN clients. Back at 1.1.0 Gatekeeper didn’t even configure your LAN interface or set up NAT rules. It used to be something like dnsmasq
- but with a web UI.
I’ve been improving it for the past couple of months - simplified it a lot, fixed bugs, optimized internals, improved the looks & added a bunch of quality-of-life features. Now it’s a program that turns any linux machine into a home internet gateway. It’s closer to OpenWRT in single executable file.
One big thing that happend was that I’ve attempted to replace the ubiquitous nft
-based NAT (where the kernel processes the packets according to a rule-based script) with nfqueue
(where the kernel sends the packet to userspace so that it can be altered arbitrarily). I’ve expected this to be buggy & slow. Well, it was hell to implement but it turns out that it’s not slow at all. On the debug build my router can push 60GiB+ / second over TCP (over virtualized ethernet of course). And I’m not even using any io_uring
magic yet. Quite honestly I don’t even know how to explain it since it’s slightly above the peak DDR4 transfer rate (I’m running dual channel DDR4-3200). Maybe the pages are not flushed to RAM & are only exchanged through CPU caches? Anyway I’m pretty excited because userspace access to all traffic opens a lot of new possibilities…
The first thing is NAT. By default Linux only supports symmetric NAT, which is pretty secure but is also fairly hard for the peer-to-peer protocols to pierce. There are some patches that make Linux full-cone but they’re not, and are not expected to become a part of the mainline kernel (at least according to OpenWRT forums). Now, since we have access to every packet we can take care of this ourselves. We can create a couple hash tables to track connections, alter the source & destination IP, recomputing the checksums if necessary. Suddenly we can have full-cone NAT, on any linux machine, without patching the kernel! At runtime it’s not as configurable as netfilter + conntrack but it’s a whole lot simpler - since now we can use a general purpose programming language rather than netfilter rules.
Another cool feature that we can now have are truly realtime traffic graphs. Summary of each packet traversing the network boundary is immediately streamed to the connected web UIs over WebSocket. This is way faster than the alternatives based on reading some /proc/
or /sys/
files every couple of seconds. Gatekeeper also aggregates the traffic from the last 24 hours between each pair of hosts into a histogram with 100ms resolution and allow clients to view it, scroll through it, compute stats, download as JSON or CSV. You can retroactively check which device talked with what IP, at what time with unprecedented resolution.
My next step is going to be capturing the traffic that goes through into a 5MB circular buffer (separate buffer for each LAN client) & downloading it as Wireshark-compatible pcap files. Computationally it’s almost free. IoT devices usually don’t transmit a lot of data. 5MB may actually cover months of traffic for the simpler ones. If any device is did anything weird, it will finally be possible to investigate it - even after it already happened.
Long-term Gatekeeper could do even more. For example offer assistance in setting up TLS MITM, perform some online grouping / analysis on the live traffic.
I still have some ground work to do - like automatically setting up Wireless LAN, bridgidng multiple interfaces into a single one and I think there may be a bug that causes crashes when checking GitHub for updates. But I wanted to share it sooner rather than later. I hope that despite its imperfections some of you will find it useful!
(I’ve had some issues with cross-instance posting. This is attempt #6)
So you’re not remapping the source ports to be unique? There’s no mechanism to avoid collisions when multiple clients use the same source port? Full Cone NAT implies that you have to remember the mapping (potentially indefinitely—if you ever reassign a given external IP:port combination to a different internal IP or port after it’s been used you’re not implementing Full Cone NAT), but not that the internal and external ports need to be identical. It would generally only be used when you have a large enough pool of external IP addresses available to assign a unique external IP:port for every internal IP:port. Which usually implies a unique external IP for each internal IP, as you can’t restrict the number of unique ports used by each client. This is why most routers only implement Symmetric NAT.
(If you do have sufficient external IPs the Linux kernel can do Full Cone NAT by translating only the IP addresses and not the ports, via SNAT/DNAT prefix mapping. The part it lacks, for very practical reasons, is support for attempting to create permanent unique mappings from a larger number of unconstrained internal IP:port combinations to a smaller number of external ones.)
Regarding port collisions. In Gatekeeper there are both - a Symmetric NAT & Full Cone NAT. Both are used in tandem. I didn’t mention the former before. Symmetric NAT takes precedence over Full Cone NAT when a connection has already been established (we observed the remote host and have a record of which LAN IP they’re talking to). You’re 100% correct that without Symmetric NAT there would be port collisions and computers in LAN would fight over ports. I actually started out with just the Full Cone NAT only (where collisions can happen) and used it on my network for a couple of weeks. It seemed to work in my home environment but I was a little worried about potential flakiness so I’ve implemented a backup mechanism eventually.
Ah, I also recalled something like that! What you’re saying about NAT assignments being permanent & requiring multiple IPs to avoid collisions. I think there was a course at my university or some Cisco course that taught that… I haven’t been able to find any online sources that would confirm those definitions today but I also remember something along the lines of what you’re describing. I have no idea what happened with those terms. Maybe the “permanent assignments” don’t make much sense in wireless networks WiFi devices can appear and disappear at any time?
Edit: I found it - the proper term for this was “Static NAT” (as opposed to “Dynamic NAT” where the redirections expire).
Right. Port preservation is not a strictly necessary part of Full Cone NAT. It’s a nice feature though. I guess the technical classification would be “Full Cone NAT with port preservation”.
This is very cool indeed. I didn’t knew that. Thanks!
I may have been a bit too strong in calling the mappings “permanent”, but still the idea behind Full Cone NAT is that once a packet has been sent out from a specific external IP:port any external host can reach the same internal source IP:port by sending packets to that external IP:port. Allowances can be made for Dynamic NAT and reclaiming ports which have been idle for a while but I really don’t think just directing packets to the most recent client to use that source port would qualify, as responses would potentially be going to the wrong internal IP. It could even allow malicious clients to trivially capture packets meant for other clients.
You could arrange to keep the same source port in most cases while redirecting to another one if the client’s preferred source port was recently used for a different IP. This would require more detailed connection tracking, however.