fix(sysctl-tuner): split the host route MTU by encryption mark #165

Merged
binjovi-bot merged 2 commits from feat/host-route-mtu-table into trunk 2026-09-02 03:11:34 +00:00
Owner

Problem

#161 lowered the DHCP route to the private network from 1450 to the cilium_wg0 MTU (1355). Host sockets now build 1355-byte segments (ss -tin: 71 sockets at pmtu:1355), but Ip: FragOKs kept climbing on every worker (10 s sample, 2026-09-02: 42afba +30,398, b6ceba +7,422, 2a9194 +6,409). WireGuard's encrypted packets take the same route: a 1355-byte segment plus 60 bytes of WireGuard framing is 1415 bytes, larger than the route's 1355, and WireGuard sends its UDP without DF, so the kernel fragments the outer packet instead of the inner one. One route needs two MTUs.

Change

Cilium gives cilium_wg0 the fwmark MARK_MAGIC_ENCRYPT (0x0E00 under MARK_MAGIC_HOST_MASK 0x0F00, bpf/lib/common.h). The reconciler now keeps:

  • routing table 900 with the DHCP gateway routes sourced from the node's own address, at the cilium_wg0 MTU (ip route replace 10.0.0.0/16 via 10.0.0.1 dev enp7s0 src <node> mtu 1355 table 900);
  • rule 900: not fwmark 0xe00/0xf00 lookup 900, so every packet without the encryption mark looks up table 900 first, and WireGuard's marked packets fall through to dhcpcd's route at 1450.

dhcpcd's route is not edited any more; the route #161 lowered is put back to the interface MTU (a one-time repair, kept because dhcpcd only re-asserts it at rebind). On opted-out nodes (the control plane) or when cilium_wg0 is gone, the table routes and the rule are removed. Stale table routes (an old lease's prefix, a changed gateway or MTU) are replaced or dropped. Reconciled every 30 s together with the CiliumNode read; a failed route listing edits nothing. Table 900 and rule 900 are clear of Cilium's (rules 9–112, tables 200/202/2004/2005/10+ifindex).

Verified end to end on a real kernel (AlmaLinux 10 container with NET_ADMIN, iproute2 6.17, the real script with only nsenter/curl/sleep faked): after one pass ip route get 10.0.0.9 reports table 900 … cache mtu 1355 and ip route get 10.0.0.9 mark 0xe00 reports cache mtu 1450; opting out, losing cilium_wg0, an interface bounce and a stray rule at pref 900 all converge in one pass with no log noise.

Guard

tests/host-route-mtu-contract.sh runs the real inline script under fake nsenter/ip/curl/sleep. The fake ip models the main table, table 900 and rule 900 the way the kernel renders and refuses them (a never-created table answers FIB table does not exist), records every edit as argv and asserts the exact sequence in 16 cases: fresh encrypting node, converged, #161's lowered route repaired, opted-out with and without a table, API unreachable / garbage / lost after a good read (mode stands, table rebuilt after an interface bounce), cilium_wg0 gone, stray rule replaced, stale table MTU / prefix / gateway, failed listing edits nothing, route without an mtu word, kernel state words stripped, clean exit within 5 s of SIGTERM. Green locally (4 s) and inside the admission image (11 s).

Mutation-checked: 37 manifest mutations each turn the test red (wrong mark, rule without not, rule always re-added, any rule accepted, rule or table kept when opted out or when cilium_wg0 is gone, table route at the interface MTU / without src, never replaced, MTU or gateway ignored, stale routes kept, failed listing read as empty, no repair / repair to the wrong MTU, filters dropped, 5 s cadence, foreground sleep, reconcile skipped, plus the wiring and CiliumNode-read mutations from #161).

Verify after deploy

  • kubectl -n sysctl-tuner logs ds/sysctl-tuner -c route-mtu: workers log dhcp route mtu 1450 (was 1355), table 900: 10.0.0.0/16 … mtu 1355, rule 900: …; the control plane logs node encryption: no and nothing else
  • workers: ip rule shows 900: not from all fwmark 0xe00/0xf00 lookup 900; ip route get <peer> shows table 900 … mtu 1355, ip route get <peer> mark 0xe00 shows mtu 1450
  • Ip: FragOKs in /proc/net/snmp stops climbing on every worker
  • Grafana namespace-resources: no throttling in sysctl-tuner

https://claude.ai/code/session_01LeVatedQ4sCA1u6LHjo7B5

## Problem #161 lowered the DHCP route to the private network from 1450 to the `cilium_wg0` MTU (1355). Host sockets now build 1355-byte segments (`ss -tin`: 71 sockets at `pmtu:1355`), but `Ip: FragOKs` kept climbing on every worker (10 s sample, 2026-09-02: 42afba +30,398, b6ceba +7,422, 2a9194 +6,409). WireGuard's encrypted packets take the same route: a 1355-byte segment plus 60 bytes of WireGuard framing is 1415 bytes, larger than the route's 1355, and WireGuard sends its UDP without DF, so the kernel fragments the outer packet instead of the inner one. One route needs two MTUs. ## Change Cilium gives `cilium_wg0` the fwmark `MARK_MAGIC_ENCRYPT` (`0x0E00` under `MARK_MAGIC_HOST_MASK 0x0F00`, `bpf/lib/common.h`). The reconciler now keeps: - routing table **900** with the DHCP gateway routes sourced from the node's own address, at the `cilium_wg0` MTU (`ip route replace 10.0.0.0/16 via 10.0.0.1 dev enp7s0 src <node> mtu 1355 table 900`); - rule **900**: `not fwmark 0xe00/0xf00 lookup 900`, so every packet without the encryption mark looks up table 900 first, and WireGuard's marked packets fall through to dhcpcd's route at 1450. dhcpcd's route is not edited any more; the route #161 lowered is put back to the interface MTU (a one-time repair, kept because dhcpcd only re-asserts it at rebind). On opted-out nodes (the control plane) or when `cilium_wg0` is gone, the table routes and the rule are removed. Stale table routes (an old lease's prefix, a changed gateway or MTU) are replaced or dropped. Reconciled every 30 s together with the CiliumNode read; a failed route listing edits nothing. Table 900 and rule 900 are clear of Cilium's (rules 9–112, tables 200/202/2004/2005/10+ifindex). Verified end to end on a real kernel (AlmaLinux 10 container with NET_ADMIN, iproute2 6.17, the real script with only nsenter/curl/sleep faked): after one pass `ip route get 10.0.0.9` reports `table 900 … cache mtu 1355` and `ip route get 10.0.0.9 mark 0xe00` reports `cache mtu 1450`; opting out, losing `cilium_wg0`, an interface bounce and a stray rule at pref 900 all converge in one pass with no log noise. ## Guard `tests/host-route-mtu-contract.sh` runs the real inline script under fake `nsenter`/`ip`/`curl`/`sleep`. The fake `ip` models the main table, table 900 and rule 900 the way the kernel renders and refuses them (a never-created table answers `FIB table does not exist`), records every edit as argv and asserts the exact sequence in 16 cases: fresh encrypting node, converged, #161's lowered route repaired, opted-out with and without a table, API unreachable / garbage / lost after a good read (mode stands, table rebuilt after an interface bounce), `cilium_wg0` gone, stray rule replaced, stale table MTU / prefix / gateway, failed listing edits nothing, route without an `mtu` word, kernel state words stripped, clean exit within 5 s of SIGTERM. Green locally (4 s) and inside the admission image (11 s). Mutation-checked: 37 manifest mutations each turn the test red (wrong mark, rule without `not`, rule always re-added, any rule accepted, rule or table kept when opted out or when `cilium_wg0` is gone, table route at the interface MTU / without `src`, never replaced, MTU or gateway ignored, stale routes kept, failed listing read as empty, no repair / repair to the wrong MTU, filters dropped, 5 s cadence, foreground sleep, reconcile skipped, plus the wiring and CiliumNode-read mutations from #161). ## Verify after deploy - `kubectl -n sysctl-tuner logs ds/sysctl-tuner -c route-mtu`: workers log `dhcp route mtu 1450 (was 1355)`, `table 900: 10.0.0.0/16 … mtu 1355`, `rule 900: …`; the control plane logs `node encryption: no` and nothing else - workers: `ip rule` shows `900: not from all fwmark 0xe00/0xf00 lookup 900`; `ip route get <peer>` shows `table 900 … mtu 1355`, `ip route get <peer> mark 0xe00` shows `mtu 1450` - `Ip: FragOKs` in `/proc/net/snmp` stops climbing on every worker - Grafana namespace-resources: no throttling in `sysctl-tuner` https://claude.ai/code/session_01LeVatedQ4sCA1u6LHjo7B5
fix(sysctl-tuner): split the host route MTU by encryption mark
All checks were successful
binjovi/ci Binjovi completed the frozen plan
2ddabf9d4b
Lowering the DHCP route MTU moved the fragmentation from the host's
segments to WireGuard's encrypted packets, which use the same route
(FragOKs still climbing on every worker, 2026-09-02). One route needs
two MTUs. The reconciler now keeps table 900 with the private gateway
routes at the cilium_wg0 MTU and a rule that sends every packet without
Cilium's MARK_MAGIC_ENCRYPT (0xe00/0xf00) through it first; WireGuard's
own marked packets stay on dhcpcd's route at the interface MTU. The
DHCP route the first version lowered is put back.

The contract test runs the real script against fake nsenter/ip/curl
with a table and rule model that answers like the kernel; 16 cases,
37 manifest mutations each red. Verified end to end on a real kernel
in a throwaway container: unmarked lookups cache mtu 1355, lookups
with mark 0xe00 cache mtu 1450.

Claude-Session: https://claude.ai/code/session_01LeVatedQ4sCA1u6LHjo7B5
Merge remote-tracking branch 'origin/trunk' into feat/host-route-mtu-table
All checks were successful
binjovi/ci Binjovi completed the frozen plan
34be07b39b
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/seanfarm!165
No description provided.