mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-08-07 04:59:49 +00:00
feat(socket): add Linux SO_MARK (fwmark) support for underlay sockets (#2288)
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>
This commit is contained in:
@@ -27,10 +27,20 @@ pub fn create_listener_by_url(
|
||||
l: &url::Url,
|
||||
global_ctx: ArcGlobalCtx,
|
||||
) -> Result<Box<dyn TunnelListener>, Error> {
|
||||
use crate::common::config::ConfigLoader;
|
||||
let socket_mark = global_ctx.config.get_flags().socket_mark;
|
||||
Ok(match l.try_into()? {
|
||||
TunnelScheme::Ip(scheme) => match scheme {
|
||||
IpScheme::Tcp => TcpTunnelListener::new(l.clone()).boxed(),
|
||||
IpScheme::Udp => UdpTunnelListener::new(l.clone()).boxed(),
|
||||
IpScheme::Tcp => {
|
||||
let mut l = TcpTunnelListener::new(l.clone());
|
||||
l.set_socket_mark(socket_mark);
|
||||
l.boxed()
|
||||
}
|
||||
IpScheme::Udp => {
|
||||
let mut l = UdpTunnelListener::new(l.clone());
|
||||
l.set_socket_mark(socket_mark);
|
||||
l.boxed()
|
||||
}
|
||||
#[cfg(feature = "wireguard")]
|
||||
IpScheme::Wg => {
|
||||
use crate::tunnel::wireguard::{WgConfig, WgTunnelListener};
|
||||
@@ -39,15 +49,20 @@ pub fn create_listener_by_url(
|
||||
&nid.network_name,
|
||||
&nid.network_secret.unwrap_or_default(),
|
||||
);
|
||||
WgTunnelListener::new(l.clone(), wg_config).boxed()
|
||||
let mut l = WgTunnelListener::new(l.clone(), wg_config);
|
||||
l.set_socket_mark(socket_mark);
|
||||
l.boxed()
|
||||
}
|
||||
#[cfg(feature = "quic")]
|
||||
IpScheme::Quic => {
|
||||
// QUIC reads socket_mark from global_ctx in QuicEndpointManager
|
||||
tunnel::quic::QuicTunnelListener::new(l.clone(), global_ctx.clone()).boxed()
|
||||
}
|
||||
#[cfg(feature = "websocket")]
|
||||
IpScheme::Ws | IpScheme::Wss => {
|
||||
tunnel::websocket::WsTunnelListener::new(l.clone()).boxed()
|
||||
let mut l = tunnel::websocket::WsTunnelListener::new(l.clone());
|
||||
l.set_socket_mark(socket_mark);
|
||||
l.boxed()
|
||||
}
|
||||
#[cfg(feature = "faketcp")]
|
||||
IpScheme::FakeTcp => tunnel::fake_tcp::FakeTcpTunnelListener::new(l.clone()).boxed(),
|
||||
|
||||
Reference in New Issue
Block a user