Key shared virtual NIC registry entries by both netns and device
name. Instances in different namespaces can no longer reuse the same
underlying device by accident.
Store each member's configured MTU separately and publish it as
the initial MTU claim after device creation. Later members no longer
inherit only the first member's MTU.
Shut down the dispatcher when the last current member leaves, and
invalidate the shared NIC if member cleanup fails. The registry entry
remains available for a later clean recreate.
Keep Magic DNS fake-IP routes owned by shared TUN members through
the shared backend. Stopping one member no longer removes the route
while another shared member still owns it, and Linux retained routes
are re-applied after address deletion.
Extend the shared TUN Magic DNS integration test to drop one shared
member first, verify DNS still works through the remaining member,
and then verify final cleanup after the last member exits.
Android previously treated setTunFd as a single-instance update, and the
VpnService plugin could only expose one IPv4 address. That made shared
TUN members disable each other or leave only one address configured.
Group enabled Android TUN instances by shared dev_name, send the fd to
every compatible member, and only disable incompatible TUN users. Build
the Android VPN request from the whole running shared group and pass
every IPv4 address to VpnService.
The shared mobile dispatcher now owns current fd device state on a
process-level runtime. New setTunFd calls replace that state even when
the raw fd number is reused, and mobile TUN read/write/create failures
rebuild with backoff while preserving member registrations.
Protect shared member cleanup with per-registration ownership tokens, so
old async cleanup cannot unregister a recreated member or remove its
source claims. Mobile source addresses are registered in the dispatcher
without applying OS ifcfg changes, so Android-originated packets return
through the owning instance.
When one shared member stops while another remains, notify the frontend
to recalculate the VpnService config instead of leaving stale addresses
and routes. Serialize Android VpnService config recalculation so stale
async events cannot overwrite newer shared-group state.
If one shared member is not ready, rebuild from the healthy members and
retry the missing member later. If no healthy member remains, stop the
Android VPN service instead of keeping stale routes active.
Cover shared tun duplicate proxy CIDR failover and runtime proxy
CIDR add/remove through netns integration tests.
Start IP proxy from config patch events so a node that did not have
proxy CIDRs at startup can serve a later proxy network patch.
Ignore NotFound for shared NIC remove-side ifcfg cleanup so member
teardown does not poison shared owner state when the OS item is
already gone.
Add dispatcher coverage for TUN read failure invalidation and member
close notification.
Add a shared tun subnet proxy topology with shared members on both
the source and destination sides. The test exercises ICMP, TCP
subnet proxy, and UDP subnet proxy against a real netns target.
Add focused KCP and QUIC TCP proxy variants that verify the selected
TcpProxyConnect transport label. The helper waits for real proxy CIDR
reachability before running transport-specific checks to avoid racing
route installation.
Add an end-to-end shared tun Magic DNS test with two members
sharing one device in the same namespace. The test checks real
netns ping paths and queries the Magic DNS fake IP through dig.
Teach DnsRunner to pass the instance netns into
MagicDnsServerInstance so fake DNS route add/remove operations run
where the tun device exists. Keep the existing constructors as
wrappers so current callers stay source-compatible.
Track each shared tun member's claimed IPv4 and IPv6 addresses in
the dispatcher.
When a packet arrives from the shared device, keep the existing flow
affinity lookup first, then route by source address owner before
falling back to an arbitrary member. This keeps public IPv6 auto
traffic on the member that owns the leased address while preserving
the simple local-origin fallback.
Migrate high-value shared tun coverage from the backup branch: real
member communication, no_tun isolation, proxy CIDR forwarding, and
public IPv6 auto through a shared device.
Add focused tests for Instance nic context creation without creating a
real tun device.
Verify empty dev_name keeps the dedicated backend, while matching
shared dev_name values reuse the same shared virtual nic and get fresh
member ids on each recreation.
Route normal, DHCP, and mobile nic creation through one helper that
selects the shared backend when dev_name is configured.
Keep dedicated nic creation as the default for configs without a
dev_name, and keep no_tun handling in the existing callers.
Store a process-wide shared virtual nic registry behind each instance
so networks with the same dev_name attach to the same shared device.
Create the shared virtual nic dispatcher from the mobile tun fd.
Later shared members attach through their ring tunnel and do not
consume another fd. Dedicated mobile nic creation stays unchanged.
Fix mobile no-magic-dns cfg so the mobile tun path checks without
enabling magic-dns.
Route shared nic IP and route configuration through member claims.
Repeated addresses or routes now touch the OS device only when the
first owner appears, and are removed only after the last owner leaves.
Forward NicBackend shared operations to the member claim layer.
Dynamic proxy and public IPv6 route updates now go through NicBackend,
so they use the same ownership merge behavior.
Clean member claims when shared member registration is dropped. Keep
raw ifcfg access for non-Linux platform cleanup only, and document
that this raw path does not carry the netns guard.
Replace full flow table clears with a small FIFO order queue so overflow evicts only older flow ownership hints.
Keep lookup read-only on the packet hot path and prune stale queue entries when a shared member unregisters.
Parse shared virtual nic flow keys directly from IP payload bytes instead of building pnet packet wrappers on the dispatcher hot path.
Use a local HashMap for flow owner lookup and clear the bounded table on overflow instead of removing a BTreeMap entry by key order.
Move shared member and flow ownership state into the dispatcher task.
Use control messages for member register and unregister events.
Keep the member table lock off the packet forwarding path.
Add the shared NIC dispatcher that owns the underlying VirtualNic tunnel.
Connect member ring tunnels through a private member table.
Track reverse flow ownership from packets written by a member.
Replies read from the shared device go back to the same member.
Packets without an owner fall back to any active member.
Mark the shared NIC invalid when the underlying tunnel closes or fails.
Notify members so their NicCtx can rebuild.
Use create_ring_tunnel_pair for member-level shared NIC tunnels.
That replaces the custom packet stream and sink implementation.
Keep one side of each ring tunnel in SharedVirtualNic's member map.
Later dispatcher code can connect those endpoints to the shared device.
Add a shared NicCtx constructor that obtains a SharedVirtualNicMember from the registry and wraps it in NicBackend::Shared.
Keep NicCtx::new on the dedicated backend path so existing runtime behavior does not change before the shared tunnel implementation is ready.
Replace NicCtx's direct VirtualNic field with NicBackend while keeping the constructor on the dedicated backend path.
Move existing device creation, ifcfg lookup, IP assignment, and public IPv6 updater calls through backend helpers so shared mode can be wired in a later commit without changing the dedicated flow.
Introduce SharedVirtualNicMember as the per-instance handle for a shared virtual NIC and add a registry helper to create members.
Add NicBackend so NicCtx can later choose between dedicated VirtualNic and shared member-backed tunnel creation without changing the existing dedicated path.
Add a small registry keyed by dev_name so shared-mode setup can reuse one SharedVirtualNic per device name.
Track invalid shared NICs through a shared flag and replace them on the next get_or_create call.
Move TUN-specific configuration into VirtualNicConfig so VirtualNic and SharedVirtualNic no longer depend on ArcGlobalCtx.
Keep global context side effects in NicCtx, including the Windows generated device-name writeback.
Remove quinn-plaintext to fix connection errors caused
by different hash values across platforms.
On x64, maintain compatibility with quinn-plaintext.
* feat(ffi): add async data plane API
* feat(ffi): add async data plane examples
* test(ffi): make async Go dataplane tests self-contained
* docs(ffi): document Go async dataplane API
* docs(android): document dataplane JNI API
pnpm v11 introduces breaking changes that cause frozen installations
to fail:
1. The "pnpm" field in package.json is no longer read. Moved
`overrides` to `pnpm-workspace.yaml` to fix
`ERR_PNPM_LOCKFILE_CONFIG_MISMATCH`.
2. `strictDepBuilds` is now enabled by default. Added required
dependencies (esbuild, unrs-resolver, vue-demi) to `allowBuilds` in
the workspace config to fix `ERR_PNPM_IGNORED_BUILDS`.
Add config server client support for the C FFI and Android JNI bindings.
Reuse the existing easytier::web_client::run_web_client path and
NetworkInstanceManager; OHOS is unchanged.
Report successful remote config apply/delete operations through a
callback, with one JSON event per affected instance.
Keep the config server client and FFI data plane mutually exclusive: once
either side is in use, the other side returns an error instead of sharing
lifecycle state.
Use pbjson to support string deserialization for enum fields
This allows TOML configs like:
chainType = "Inbound"
instead of:
chainType = 1
- Maintain backward compatibility with integer values
- Default serialization format is now string
TomlConfigLoader::new_from_str() always calls NetworkIdentity::new()
with unwrap_or_default() on network_secret, converting None to ''.
This creates a non-zero SHA256 digest, causing credential nodes loaded
from TOML to be misidentified as regular nodes (with network_secret),
which breaks Noise handshake authentication.
Fix: check if secure_mode is enabled AND network_secret is absent/empty,
and call NetworkIdentity::new_credential() in that case.
The same detection already exists in:
- core.rs (CLI path, via --credential flag)
- launcher.rs (GUI/web path, via gen_config)
This makes TOML config loading consistent with the other two entry points.
1. Overview
This PR adds data plane APIs to easytier-ffi:
TCP Outbound:
- data_plane_tcp_connect
- data_plane_tcp_read
- data_plane_tcp_write
- data_plane_tcp_close
TCP Listener:
- data_plane_tcp_bind
- data_plane_tcp_accept
- data_plane_tcp_listener_close
UDP:
- data_plane_udp_bind
- data_plane_udp_send_to
- data_plane_udp_recv_from
- data_plane_udp_close
2. Key Changes
The main changes are focused on:
- easytier-contrib/easytier-ffi/src/lib.rs: Added FFI interfaces;
made ERROR_MSG thread-safe.
- easytier/src/gateway/socks5.rs: Bridges the data plane to the
existing Socks5 server logic.
- Added EasyTierUdpSocket, mainly wrapping ref-counting and
critical object (e.g., Socks5EntrySet) hold & drop logic,
and exposing common fields (e.g., local_addr).
- Extended Socks5Server functionality to expose TCP and UDP
socket creation interfaces for FFI calls.
- Other files: Mostly pass-through logic.
- Added a relatively large Go usage example.
Adds a Linux-only socket_mark u32 config flag (CLI: --socket-mark, env:
ET_SOCKET_MARK, TOML/proto: flags.socket_mark, 0 = disabled) that is
applied as SO_MARK to every outbound underlay socket EasyTier creates:
TCP, UDP, QUIC, WebSocket, WireGuard connectors and listeners, plus the
FakeTCP decoy socket. Lets the host policy-route or filter EasyTier
underlay traffic with 'ip rule fwmark ...' or iptables -m mark.
Plumbing mirrors the existing bind_device pattern:
- FlagsInConfig.socket_mark (proto) + default 0 in gen_default_flags
- bind() builder gets a socket_mark arg; setup_socket2_ext calls
apply_socket_mark which is a no-op for mark=0 and on non-Linux
- TunnelConnector trait gets set_socket_mark(u32) default-no-op method
- IP-based connectors override; create_listener_by_url and the connector
factory pass mark from global_ctx flags
- QUIC threads mark through QuicEndpointManager::{server,connect}
- WebSocket/FakeTCP/TCP default-bind bypass paths apply mark via
socket2::SockRef::from(&tokio_socket)
- ForeignNetworkEntry propagates parent socket_mark into its derived ctx
Includes a Linux smoke test plus a CAP_NET_ADMIN-gated test that does a
getsockopt(SO_MARK) round-trip to confirm the kernel applied the value.
SO_MARK requires CAP_NET_ADMIN; ignored silently on non-Linux. FakeTCP's
TUN-written segments are not covered (kernel doesn't tag raw TUN
writes); operators relying on fwmark for FakeTCP must apply an iptables
rule on the FakeTCP TUN device separately.
Co-authored-by: Claude <noreply@anthropic.com>
Fix the issue where the Magisk module incorrectly matches the lookup main rule in Android 15's cellular network rules, causing data plane connectivity failure
This may helps games to find rooms in virtual network.
- add opt-in Windows UDP broadcast relay config flag and CLI/env plumbing
- capture local UDP broadcasts with Windows raw sockets, normalize packets, and inject them via PeerManager