Remote access via the second uplink fails while the configured default gateway is down #134
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: High. Found in the code review of the commits from 2026-11-04 to 2026-11-11 (
mainat6076be32). Still present onmainatadbeb2f4. Reproduced in a router VM, with the cause confirmed.Requirement (from the user)
Context
Reply-to (
f96fcd5e, ADR 0026): connections that arrive on an uplink get a conntrack label. Their replies get the uplink's policy mark, and the fwmark rule at priority 100 selects the gateway table.Shared subnet (
c38df1d6, ADR 0027, #120): uplink tables carry their connected routes, and source routing installs rules per uplink address.53a0d664made it three rules per address on consecutive priorities:from A lookup <source table> suppress_prefixlength 0from A lookup main suppress_prefixlength 0(new)from A lookup <source table>This fixed LAN clients that connect to an uplink address. Their replies had left through the uplink's gateway.
Fail-closed: when all configured default gateways are down, Kaido installs
unreachable default proto 201 metric 1in main (internal/service/gateway_group.go:421–433). Traffic then does not escape through lower-priority DHCP/RA defaults.Problem
suppress_prefixlengthonly when the table lookup succeeds (net/core/fib_rules.c:342–352).unreachableroute returns-EHOSTUNREACH(net/ipv4/fib_semantics.c:117,net/ipv4/fib_trie.c:1566), so the rule walk ends with an error and never reaches rule 3.53a0d664, the source table answered these packets correctly.Impact
default_gateways= WAN1 only and WAN2 as a secondary uplink. The uplinks are DHCP-discovered, because source routing applies only to them.Evidence (router VM, image
6076be32, two DHCP uplinks,default_gateways = ['WAN1_GW4'], remote client 192.0.2.10 behind WAN2)ip route get 192.0.2.10 from 203.0.113.2givesvia 203.0.113.1 dev eth2 table 2001. From the remote client, ping gets 3 of 3 and TCP 8443 connects.unreachable default proto 201 metric 1.ip route get … from 203.0.113.2then givesRTNETLINK answers: Host is unreachablein 52 of 52 samples.204: from 203.0.113.2 lookup main suppress_prefixlength 0immediately sends the route viatable 2001, and ping and TCP from the remote client work.Proposed change
throwroute instead ofunreachable. This touchesinstallMainUnreachableRouteand the audit/reconcile predicates for it.RTN_THROWreturns-EAGAIN(net/ipv4/fib_semantics.c:125,net/ipv6/route.c:1101), and the rule walk then continues (net/core/fib_rules.c:348,net/ipv6/fib6_rules.c:250–259).defaultis empty. The lookup ends withENETUNREACH, so fail-closed still holds.Rejected alternatives
53a0d664: brings back the bug where a LAN client cannot reach the dashboard on an uplink address (#120 context).Open question: manual gateways
88813470(April 2026), without a documented reason; ADR 0028 only records the consequence.throwfalls through to the emptydefaulttable, and the first lookup still fails.rp_filter=2may also drop the incoming request.Acceptance tests
0.0.0.0:9443(notbind_interface = 'lan'as inexample-router.toml:3106) and a firewall that allows 9443 from any source. Checks:default_gateways= WAN1 only and once with a WAN1→WAN2 failover group;53a0d664case).TestIsolatedReplyTo*(including shared subnet, #120) andTestIsolatedSourceRouting*stay green.References
53a0d664,c38df1d6,f96fcd5e,6076be32,88813470.Baseline comparison on the build VM (2026-11-12)
Setup.
0.0.0.0:9443, and the firewall allows 9443 and ICMP from any source.States:
set_link.Systems:
6076be32, Alpine 3.24.4, kernel 6.18.63-virt.Results (TCP connects from the remote client to the dashboard port, 10 attempts each)
default_gateways= WAN1default_gateways= gw-wan1Further results:
Findings
rp_filter=1drops every request that arrives on WAN2. The guest'sIPReversePathFiltercounter rose by 33 per probe run.rp_filter=2. This is exactly what Alpine lacks.unreachable default proto 201 metric 1appears in main.ip route get 192.0.2.10 from 203.0.113.2givesRTNETLINK answers: Host is unreachable.all.rp_filter=0set by hand: the SYNs reach the VM, but there is still no SYN-ACK and route get stays unreachable.default via 203.0.113.1 dev eth2 proto 201 metric 1.Consequence for the fix
throwalone is not enough:throwfalls through to the emptydefaulttable and both gaps remain.The fix therefore has two parts:
throwroute instead ofunreachable.return_routing. Update ADR 0028, which today says "source routing covers discovered gateways only", and the gateway form text.The acceptance test runs all five Kaido variants above. In S1 and S2, the uplink that is still up must answer.