Files
EasyTier/docs/socket-protection.md
T
38e2a621bb refactor(ohos): 拆分 OHRS 包并按 socket 精细保护 VPN 流量 (#2543)
* refactor(ohos): split facade feature and kernel crates
* feat(ohos): protect transport sockets individually
* fix(ohos): keep local proxy subnets off tun
* fix(ohos): expose valid config enum values
* refactor(ohos): finalize reusable core boundary
* test(ohos): verify split package contracts

* fix(port-forward): support wildcard userspace listeners
Keep the existing Host listener intact while adding a DataPlane listener for force-smoltcp IPv4 wildcard rules. Keep literal loopback destinations on the local Host path instead of exporting them through an exit node.

* chore(ohos): refresh split workspace lockfile
* fix(socket): normalize Windows raw socket handles
* refactor(socket): carry VPN protection through host bind options
* refactor(socket): simplify protection defaults and TUN ingress
* refactor(socket): consolidate native protection and socket creation
* fix(socket): protect outbound UDP paths
* fix(socket): preserve VPN routing for RPC listeners

---------

Co-authored-by: FrankHan <frankhan@FrankHans-Mac-mini.local>
Co-authored-by: KKRainbow <443152178@qq.com>
2026-09-09 22:12:47 +08:00

85 lines
5.0 KiB
Markdown

# Host socket protection
VPN bypass is a socket-creation requirement, not an operation on a socket that
core has already connected. Core and WASI guests never need an OS file descriptor.
## Portable requests
`TcpBindOptions::need_protect` and `UdpBindOptions::need_protect` travel through the
existing connect/bind operations. A host requiring VPN bypass must acknowledge
successful protection before connect, bind/listen, or publishing the socket.
Failure or cancellation fails creation and discards the owned socket; emitting
an event alone is not acknowledgement. Platforms without VPN bypass can treat
the requirement as a no-op.
- TCP connect constructors request protection, including egress for proxies.
- TCP transport/hole-punch listeners request protection. Hosts retain this flag
on the listener and protect accepted children before handing them to core.
- Local ProxyNat/SOCKS/port-forward/port-lease listeners do not request it.
- UDP transport, candidates, listeners, NAT egress and STUN request protection;
HolePunchControl, local SOCKS/port-forward and port-lease sockets do not.
- Low-level TCP/UDP bind options default to protection, including deserialization
of options without `need_protect`. The named local/TUN-facing constructors set
`false` explicitly. The UDP default keeps its historical purpose label for
socket setup; only the named `hole_punch_control()` constructor opts out.
- `with_bind()` replaces the **entire** bind object. A local listener's replacement
must retain its opt-out rather than inherit the protected default. A native
adapter must honor explicit `false`, not silently change it based on `purpose`.
- DNS and source-route queries are already host-owned operations. A bypass-enabled
host must protect their underlying sockets before querying/probing, including
DNS TCP fallback, rather than letting system DNS silently bypass this contract.
## TUN-facing ingress and port forwarding
The extra `force_smoltcp` wildcard port-forward ingress listener has been removed.
Normal TUN-backed ingress is delivered to the existing unprotected native listener;
its accepted sockets remain unprotected so overlay replies can return through TUN.
The physical/underlay egress socket is protected independently. A separate
DataPlane listener must not mask broken host/TUN routing in this path.
This does not remove the existing public DataPlane listener APIs, the generic
no-TUN smoltcp TCP proxy, or `force_smoltcp` itself. Those have other uses. Whether
Android subnet proxy works without forced smoltcp needs actual platform regression
testing; socket-creation unit tests alone do not establish that result.
## Native integration and HarmonyOS
The native `easytier` adapter implements the creation requirement using an async
`NativeSocketProtector` callback. This is a native implementation detail, not a
new portable or WASI ABI. It takes only the native handle; the creation options
select policy, without a second purpose enum. Namespace switching is confined to synchronous socket
creation and never held across the callback's await.
The existing native `bind` builder is async and shared by TCP/UDP creation. Its
legacy direct-call default remains unprotected; portable factories explicitly
pass their core bind options (default protected). Callers must await `.call()`.
TCP listeners reuse TCP socket creation then listen, instead of duplicating the
setup. Existing legacy WebSocket direct-call policy is unchanged.
The HarmonyOS broker wakes the already-pending request consumer with `Notify`
(no polling timer). It keeps a duplicate FD alive until ArkTS completes
`VpnConnection.protect(fd)` and returns its ACK. The waiting creation future is
woken immediately by the oneshot acknowledgement. Failure stays fail-closed;
shutdown retains dispatched FDs for late ACKs to prevent FD reuse races.
The ArkTS request shape is unchanged; its diagnostic `purpose` string is now
the generic `"socket"`. Neither ACK routing nor protection policy uses that label.
This guarantees ordering, not a wall-clock real-time bound: OS/ArkTS scheduling
can still delay protection. Such a delay keeps the socket unconnected; it must
never allow the first SYN/query to race ahead of protection.
## WASI option format
The `easytier_host` import names and function signatures are unchanged. TCP
connect, TCP listen and UDP bind use option document **version 3** (previously 2):
one `u8` boolean `need_protect` is inserted immediately after the existing purpose
byte and before the optional bind-device field. All other field encodings and
purpose values are unchanged. The document version lets older hosts reject
unsupported options instead of silently ignoring protection. DNS, environment,
instance-config and data-plane layouts/versions are unchanged.
An embedding host must update its versioned option decoder and honor the flag
inside its existing creation implementation. The external host implementation
is not in this repository: building the guest proves propagation/compatibility
of imports, not that every external host has implemented platform protection.