feat(tunnel): IPv6 GUA pinholes — PCP v6 + manual forwards (PR2)#3403
Open
helix-nine wants to merge 5 commits into
Open
feat(tunnel): IPv6 GUA pinholes — PCP v6 + manual forwards (PR2)#3403helix-nine wants to merge 5 commits into
helix-nine wants to merge 5 commits into
Conversation
Open inbound to a client's own IPv6 GUA on the tunnel: PCP v6 MAP requests
(over a wg-bound v6 socket) and a manual pinhole CRUD surface. A pinhole is a
pure ip6 firewall accept (no NAT); external != internal (e.g. 80->443) becomes
a port-only DNAT on the same GUA. The PCP MAP's Suggested External Port carries
the remap, so no protocol change is needed.
- db: Pinhole {label,enabled,count,internalPort,auto} + pinholes6 map (serde
default, no migration)
- net/port_map/server: handle6 + v6 GatewayBackend methods (default-off)
- tunnel/forward/pinhole: is_known_gua (host_v6 reverse), nft_rule_v6 rule
build, db persistence, startup seed
- tunnel/forward/pcp: v6 socket + serve loop, TunnelContext v6 impl
- api: pinhole add/remove/update-label/set-enabled (+ i18n, manpage)
Add addPinhole/deletePinhole/updatePinholeLabel/setPinholeEnabled to the tunnel ApiService (live + mock), seed a mock pinholes6 entry, and regenerate the osBindings for the new Pinhole/Pinholes6/*PinholeParams types. The unified v4+v6 forward dialog builds on this next.
Rework the port-forward dialog into one form covering both stacks (start-wrt Published Ports pattern): an IP Version radio (IPv4 / IPv6 / IPv4 + IPv6), the external/internal port fields applying to whichever is picked, GUA-gated for v6. IPv6 saves a pinhole (or a port-DNAT when external != internal); the also-80 checkbox opens the 80->443 on each selected version. The forwards list now merges port_forwards + pinholes6 with an IP column, and toggle/delete/relabel route to the pinhole API for v6 rows. Devices carry their computed GUA.
Mirror the v4 redirect_maps for IPv6: when a GUA exposes 443, also ask the gateway for an 80->443 redirect pinhole (withdrawn when 443 stops being exposed or 80 becomes a real pinhole). Tracks candidate v6 gateways per GUA so the post-loop redirect reaches the same gateways the pinholes used.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR2 — IPv6 GUA pinholes (PCP v6 + unified v4+v6 forwards)
Follow-up to #3388 (per-subnet IPv6 addressing). Opens inbound to a client's
own IPv6 GUA on the tunnel — the piece that makes v6 hosting work end-to-end —
and unifies v4 forwarding and v6 pinholes behind one dialog.
What a "pinhole" is
A client already gets a
/128GUA on any IPv6-enabled subnet (#3388). A pinholeaccepts inbound to that GUA on a port — a pure
ip6 startosfirewall accept,no NAT (the GUA is directly routable, and the OS-side reply-routing sends the
return out the arrival interface). When the external port differs from the
internal (e.g. an 80→443 redirect) it becomes a port-only DNAT on the same
GUA. Because each client has its own GUA, external ports only need to be unique
per-GUA, so two clients can both publish :443.
Two ways a pinhole is opened
socket;
handle6forces the target to the sender's own GUA and honors the PCPSuggested External Port field, so a remap needs no protocol change. StartOS
servers already request GUA pinholes; this PR also adds the OS-side auto
80→443 redirect on v6 (mirroring the existing v4
redirect_maps).pinhole add|remove|set-enabled|update-labelCLI/RPC surface,and — in the UI — the reworked forward dialog.
UI (unified v4 + v6)
The Add Port Forward dialog now has an IP Version radio (
IPv4/IPv6/IPv4 + IPv6); the external/internal port fields apply to whichever is chosen,IPv6 is gated on the server having a GUA, and the also-80 checkbox opens the
redirect on each selected version. The forwards tables gained an IP column,
and toggle/delete/relabel route to the pinhole API for v6 rows.
Backend
db:Pinhole { label, enabled, count, internalPort: Option<u16>, auto }+pinholes6map (serde(default), no migration).net/port_map/server:handle6+ v6GatewayBackendmethods (default-off).tunnel/forward/pinhole:is_known_gua(reversehost_v6), nft rules vianft_rule_v6, db persistence, startup seed.tunnel/forward/pcp: v6 socket + serve loop,TunnelContextv6 impl.net/net_controller: OS-side v6gua_redirect_maps(auto 80→443).api: manual CRUD (+ 5-locale i18n, regenerated manpage);ApiServicemethods (live + mock) + regenerated osBindings.
port-forwarding.md,cli-reference.md,ipv6.md, CHANGELOG.Testing
cargo check -p start-coreclean; 25 tunnel + 6 pinhole/handle6unit testspass;
check:tunneland the fullbuild:tunnel(Angular AOT) green; prettierclean.
prefix (a local libvirt VM can't provide one) — worth a real-VPS pass before
release, same caveat as feat(tunnel): IPv6 support — prefix delegation + PCP pinholes (WIP) #3388.