diary @ telent

Turning the nftables#

Fri 02 Jun 2023 10:16:32 PM UTC

Topics: nix liminix

In the course of Liminix hacking it has become apparent that I need to understand the new Linux packet filtering ("firewall") system known as nftables

The introductory documentation for nftables is a textbook example of pattern 1 in Julia Evans Patterns in confusing explanations document. I have, nevertheless, read enough of it that I now think I understand what is going on, and am ready to attempt the challenge of describing

nftables without comparing to ip{tables,chains,fw}

We start with a picture:

This picture shows the flow of a network packet through the Linux kernel. Incoming packets are received from the driver on the far left and flow up to the aplication layer at the top, or rightwards to the be transmitted through the driver on the right. Locally generated packets start at the top and flow right.

The round-cornered rectangles depict hooks, which are the places where we can use nftables to intercept the flow and handle packets specially. For example:

The picture is actually part of the docs and I think it should be on the first page.

Chains and rules

A chain (more specifically, a "base chain") is registered with one of the hooks in the diagram, meaning that all the packets seen at that point will be sent to the chain. There may be multiple chains registered to the same hook: they get run in priority order (numerically lowest to highest), and packets accepted by an earlier chain are passed to the next one.

Each chain contains rules. A rule has a match - some criteria to decide which packets it applies to - and an action which says what should be done when the match succeeds.

A chain has a policy (accept or drop) which says what happens if a packet gets to the end of the chain without matching any rules.

You can also create chains which aren't registered with hooks, but are called by other chains that are. These are termed "regular chains" (as distinct from "base chains"). A rule with a jump action will execute all the rules in the chain that's jumped to, then resume processing the calling chain. A rule with a goto action will execute the new chain's rules in place of the rest of the current chain, and then the packet will be accepted or dropped as per the policy of the base chain.

[ Open question: the doc claims that a regular chain may also have a policy, but doesn't describe how/whether the policy applies when processing reaches the end of the called chain. I think this omission may be because it is incorrect in the first claim: a very sketchy reading of the source code suggests that you can't specify policy when creating a chain unless you also specify the hook. Also, it hurts my brain to think about it. ]

Chain types

A chain has a type, which is one of filter, nat or route.

Tables

Chains are contained in tables, which also contain sets, maps, flowtables, and stateful objects. The things in a table must all be of the same family, which is one of

There's a handy summary in the docs describing which chains work with which families and which tables.

What next?

I hope that makes sense. I hope it's correct :-). I haven't explained anything about the syntax or CLI tools because there are perfectly good docs for that already which you now have the background to understand.

Now I'm going to read the script I cargo-culted when I wanted to see if Liminix packet forwarding was working, and replace/update it to perform as an adequate and actually useful firewall

nftables or fn stable#

Tue 20 Jun 2023 08:11:36 PM UTC

Topics: liminix

Hadn't realised it had been so long since I last updated this, but progress has been slow lately.

liminix-rebuild seems to work nicely, and I have been dogfooding by using it to get the rotuer configuration into a shape that I could actually use it. So far: it runs PPPoE, gets IPv4 and IPv6 addresses, gets an IPv6 prefix using DHCP6 and advertises that prefix to devices on the LAN - so, albeit in an unpolished (verging on sketchy) fashion, it kinda sorta works. A simple firewall is currently in development, after having spent some time figuring out how nftables works.

Needed: (1) more polish; (2) better organisation. Once it works, lots of this stuff will need lifting into modules or services or both - or some other new yet-to-be-discovered abstraction - but the dependency there is on making it work first. I'm thinking to do an L2TP-based config as well as PPPoE (and maybe something with a wireguard VPN), which I think will expose more ordering/dependency requirements.

Housekeeping

Since NixOS 23.05 is now a thing, I'm also in the middle of making Liminix build/work with it. There's been a CI job running against unstable since I started, which has reduced the number of surprises, but still had a few things to work through:

We didn't start the fire(wall)#

Mon 26 Jun 2023 10:25:12 AM UTC

Topics: liminix

tl;dr:

First news for this week is that Liminix builds green in CI for all targets, and boots successfully on the GL-AR750. The only thing left to do on that score since last week was fix a bug in the preinit code that was not NULL-terminating an array correctly. It's presumed to boot also on the other hardware but I haven't tested it in prod yet.

The focus of development this week has been "CPE wifi router by any means possible", with the intention that we will extract modules and services from the running system once we have ... a running system. And I think we're nearly there.

To shorten the ntables development feedback loop, which often involves finding out that something doesn't work because it's missing from the kernel, I've added some code for building kernel modules without having to rebuild the kernel. This means we can add kernel modules as service dependencies and they will be loaded/unloaded as required by the service. It also means we can liminix-update to test new versions without having to reflash the kernel.

liminix-update could still be faster if it didn't have to reboot: resetting the board seems to take an age. I can see two avenues of investigation here:

The former of those would be more use to me currently: the only need I've had yet to install packages that aren't used by services is "oh, I'm missing tcpdump and strace".

....

The other area that needs some love is the new user/onboarding experience: the User Manual describes some things as they were in March, some things as they will be in The Future, and some things not at all. Going to give that some proper thought as soon as I have a router sufficiently functional that I can use it as I write.