Document the -p/--port-forward extern-connectivity fix
Records the root-cause investigation and fix for -p/--port-forward not reaching a server on an extern network (host root had no route to the container subnet, and the route alone wasn't consulted without a matching ip rule -- Android's policy routing has no default "lookup main" rule), plus the related uplink-rollback-on-failure robustness bug found while testing it, matching the level of detail already recorded for the other networking fixes in this document. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
@@ -799,6 +799,54 @@ Source layout (all under `src/`):
|
||||
that prefix rotates every ~10 minutes on the network tested against) — so
|
||||
it stays local-only by deliberate decision, matching what `intern`'s IPv6
|
||||
side already was.
|
||||
|
||||
**Resolved: `-p/--port-forward` had no connectivity at all on an `extern`
|
||||
network.** Reported after the connectivity/multi-network-join fixes above
|
||||
had already shipped: a server listening on an `extern` network wasn't
|
||||
reachable via `-p`, from the host or from a real outside client, even
|
||||
though the network's own gateway/outside connectivity (verified above)
|
||||
worked fine. Root cause, confirmed by direct testing (`ip route get
|
||||
<container-ip>` from host root): moving `extern`'s bridge into a private
|
||||
namespace fixed gateway reachability but also meant host root had no route
|
||||
to the container subnet *at all* — it fell through to whatever the host's
|
||||
own default route happened to be (the real LAN gateway) — so `-p`'s own
|
||||
`DNAT` rule (added in host root, targeting the container's real IP
|
||||
directly — `port_forward.cpp`) had nowhere to send the rewritten packet.
|
||||
Fixed with two pieces in `ensure_uplink_provisioned()`, both confirmed
|
||||
independently necessary by direct testing — the exact same "a route alone
|
||||
isn't enough on Android" lesson the uplink's own outbound/return-path `ip
|
||||
rule`s above already learned, just for the container subnet instead of the
|
||||
transit subnet: a host-root route to the container subnet through the
|
||||
uplink's own netns-side address, plus a matching `ip rule add priority 100
|
||||
to <container-subnet> lookup main`. Without the second piece, the route
|
||||
added by the first is silently never consulted: confirmed via `ip rule
|
||||
show` on the real device that Android's own lower-priority-number policy
|
||||
rules — a generic `fwmark 0/0x10000 lookup 99` catch-all among them,
|
||||
matching any untouched/forwarded packet — intercept the packet and route
|
||||
it into an unrelated table with no route to the container subnet, long
|
||||
before rule evaluation would ever reach `main`. **A related robustness bug
|
||||
found while testing this fix, not the original bug itself**: a failed
|
||||
`ensure_uplink_provisioned()` used to only call `stop_tap_relay()`,
|
||||
leaving every `ip rule`/`iptables` piece already added (all deterministic,
|
||||
hash-derived names tied to the network name) live on the host — reproduced
|
||||
directly during testing (an incidental collision between two concurrent
|
||||
test invocations triggered a first failure, whose leftover state then made
|
||||
every subsequent attempt for the same network name fail identically and
|
||||
permanently, "File exists" on an `ip rule add` that was never removed,
|
||||
until fixed by hand or a full device reboot). Fixed by recording the
|
||||
relay's pid to the uplink state file as soon as it's known (before any of
|
||||
the steps that can fail), so a failure can call the exact same
|
||||
`teardown_uplink_state()` a real `--delete-network-full` would use to roll
|
||||
back everything already added, instead of a second, partial, drifting copy
|
||||
of that cleanup logic. **Verified end-to-end on the real target device**:
|
||||
a busybox `httpd` on a freshly (re-)created `extern` network, reached via
|
||||
`-p 18080:80` both from the device's own shell (against its real LAN IP,
|
||||
not `127.0.0.1` — see `port_forward.h`'s own already-documented
|
||||
NAT-hairpinning limitation below for why that specific case still doesn't
|
||||
work, unrelated to this fix) and from a genuinely separate external
|
||||
machine on the same LAN, got a real HTTP response back both times,
|
||||
reproducibly across repeated fresh network creations.
|
||||
|
||||
`probe_veth_support()` (added for the tap+relay fallback, see
|
||||
`docs/networking-design.md`'s addendum and `network_join.{h,cpp}` below):
|
||||
the real target device supports `tun`/`tap` but not `veth`
|
||||
|
||||
Reference in New Issue
Block a user