# mvirt 0.24.0

migrations: no
wire: unchanged
rev: 1d003db1edcb0cfd7840f0b70f1982695d1d68e0

v0.24.0 — three dataplane fixes


A MINOR rather than a patch because #314 adds `member_network` to the route-mesh
NextHop. The `wire:` line above is the STORAGE wire, which nothing here touches.

The control plane goes first, then the nodes one at a time. Not a hard break:
an old cplane between new nodes strips `member_network`, so memberships land in
SG_SCOPE_LEGACY and behave exactly as they did before — old exposure, no outage.
New node against old node folds by address, i.e. the old behaviour. The window
drains once every host is new.

A floating IP's /32 lives in the global table every node subscribes to, so an
on-fabric client reaches it east-west, where the north-south default-route gate
that held the only reverse never runs. The reply left with the private source and
the client discarded it. Every floating IP was unreachable from inside the fabric.
`vip_dnat_deliver` now records the flow in NAT_CT and the egress classifier
restores the VIP per flow, exactly as it already did for LB replies.

IP_TO_SGS was keyed on the bare address, so two networks sharing a private prefix
— the normal case, since every project's `default` network is 10.0.0.0/16 —
unioned their memberships, and a `from <SG>` rule in one network admitted a
stranger at the same address in the other. The network is now carried end to end,
as #307 did for the FIB.

A guest's DNS query to the gateway resolver was answered locally AND forwarded.
An AF_PACKET socket is fed from ptype_all, which runs ahead of the TC ingress
hook, so the responder answered and the same frame carried on into tc_egress,
matched the 0.0.0.0/0 default and left the host SNATted. Resolution never broke —
the local answer always won the race — so what it cost was a duplicate of every
guest DNS query, carrying the question name in cleartext to a third party from an
address that identifies the tenant's network. tc_egress now consumes any frame
addressed to 169.254.0.0/16 or fe80::/10 and counts it in a new EGRESS_DROPS slot.

That slot reads the opposite way to its neighbours: it is not a fault, it tracks
guest DNS volume, and it should always be moving. A flat slot 3 on a node with
running guests means the guards are not being reached.

Known residual, filed as #354: a guest using its network's OWN gateway address as
its resolver still takes the old path. DHCP announces the link-local resolver
alone (ADR-0084 D10 D1), so nothing the platform hands out lands there.

The A2 pin review. `nix flake update` would move nixpkgs under three targeted
dataplane fixes and invalidate the gate runs they were proven with; the pins ride
the next minor that is a feature train.

## Upgrading

Point your flake's `mvirt` input at this release and redeploy the control-plane
host first, then each node in turn:

```
nix run .#colmena -- apply boot --reboot --on <control-plane host>
nix run .#colmena -- apply boot --reboot --on <node>   # one at a time
```

This release adds no migrations, so the control-plane host rolls back the same way
a node does: boot the previous NixOS generation.
