Designing the Monostack FutureDesign docs and notes

Minimum viable SIIT device

Idea: Re-use existing kernel functionality (netfilter, conntrack etc.) instead of reinventing it. Cut down configuration options to a bare minimum. Ideally none. Keep it maintainable as fuck.

Jool has many, many options because (among other reasons) it implements its own connection state tracking. We can do better than that.

Our current plan entails even hardcoding the one true well-known prefix 64:ff9b:1::/96 in the driver and statelessly NATing this prefix using netfilter snat/dnat as needed. This may or may not be a good idea. It's a starting point. Discussions and code welcome.

Compose NAT64 in userspace

Idea: Implementing NAT64 is much work. Linux already has well tested and performant NAT66 and NAT44. We should make use of that.

Our plan is not to integrate specific NAT64 code into the kernel (for now) but rather compose the NAT64 service in userspace using regular netfilter NAT66 (stateless) and NAT44 (stateful) on top of our new SIIT device. See also Dave's OG kernel NAT64 design.

While that sounds hacky, and it is, it only impacts the service provider side of things. We figure requiring a bespoke firewall configuration to run NAT64 services isn't really a problem these days thanks to network namespaces <3

CLAT without L2 hacks

Problem: CLAT needs to work on any random LAN. How do you aquire a dedicated address (or if the RFCs are to be adhered to even a whole /64 prefix) to use for SIIT? Ideally we'd also do translation without turning hosts into routers (enabling the IP forwarding sysctls), a problem caused by our translation device approach.

Contemporary implementations like clatd (and supposedly MacOS) use NDP proxy hacks to do this. We'd like to avoid that if at all possible.

Idea: Share regular system IPs by tightly integrating with conntrack. Perhaps even implement socket level SIIT translation but we think that may be much harder and less readily deployable. Future-Work^TM.

A PoC is TBD. Let's set the CLAT scene. Imagine a system with a siit0 hairpin translation device pre-configured for the network's NAT64 prefix, say 64:ff9b:1::/96. ip -br addr:

eth0    UP      fe80::1/128, 2001:db8::1/128
siit0   UP      fe80::1/128, 192.168.0.1/32

ip route:

default dev siit0 src 192.168.0.1

Let's follow an internet destined packet generated by a v4 socket:

  1. socket egress, siit0 ingress. src 192.0.0.1 dst 1.2.3.4

  2. siit0 egress, src 64:ff9b:1::192.0.0.1 dst 64:ff9b:1::1.2.3.4

  3. siit0.forwarding=1 routes this packet to eth0

  4. in postrouting hook: apply masquerade to remove internal CLAT src addr. i.e. now
    src 2001:db8::1 dst 64:ff9b:1::1.2.3.4.

  5. packet returns from network.
    ingress on eth0. src 64:ff9b:1::1.2.3.4 dst 2001:db8::1

  6. conntrack rewrites to dst 64:ff9b:1::192.0.0.1

  7. eth0.forwarding=1 (undesirable) routes packet to siit0

  8. siit0 ingress src 64:ff9b:1::1.2.3.4 dst 64:ff9b:1::192.0.0.1

  9. siit0 egress, socket ingress src 1.2.3.4 dst 192.0.0.1

So how do we get rid of at least eth0.forwarding=1?

Idea: maybe netdev ingress/egress hook i.e. nft fwd. We're not sure we can still rely on conntrack if we do that because of how early the netdev hook is. Not sure whether later hooks run after fwd? WIP WIP WIP.

Ondřej's IPVLAN based CLAT design is based on a different approach but is also a nice option for deployability. IPVLANs just hook into an existing device so we can add/manage them without disturbing other network config tools.

Ensuring IPv6 privacy

Problem: IPv6 privacy addressess are still not universally supported (AFAWK).

FIXME: Describe Daniel's nft privacy hack: https://gist.github.com/DanielG/c9ffeefbf50bfe4925aa204f9dc4c40a

Maybe work on reviving https://datatracker.ietf.org/doc/draft-yhb-6man-ra-privacy-flag/?

Last changed: 2025-12-02