diff --git a/README.md b/README.md index 576bbd618f8a..c0941ae02fd5 100644 --- a/README.md +++ b/README.md @@ -815,6 +815,8 @@ Example configuration: "outbound_bind_interface": "eth1", // Outbound socket bind() to this IP (choose a specific interface) "outbound_bind_addr": "11.22.33.44", + // Outbound UDP socket allows IP fragmentation (default false) + "outbound_udp_allow_fragmentation": false // Balancer customization "balancer": { diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index c958371c55e0..d5f61c9c4846 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -215,6 +215,9 @@ struct SSConfig { #[serde(skip_serializing_if = "Option::is_none")] outbound_bind_interface: Option, + #[serde(skip_serializing_if = "Option::is_none")] + outbound_udp_allow_fragmentation: Option, + #[serde(skip_serializing_if = "Option::is_none")] security: Option, @@ -401,6 +404,9 @@ struct SSServerExtConfig { #[serde(skip_serializing_if = "Option::is_none")] outbound_bind_interface: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + outbound_udp_allow_fragmentation: Option, } #[cfg(feature = "local-online-config")] @@ -1240,6 +1246,7 @@ pub struct ServerInstanceConfig { pub outbound_fwmark: Option, pub outbound_bind_addr: Option, pub outbound_bind_interface: Option, + pub outbound_udp_allow_fragmentation: Option, } impl ServerInstanceConfig { @@ -1252,6 +1259,7 @@ impl ServerInstanceConfig { outbound_fwmark: None, outbound_bind_addr: None, outbound_bind_interface: None, + outbound_udp_allow_fragmentation: None, } } } @@ -1336,6 +1344,8 @@ pub struct Config { pub outbound_bind_interface: Option, /// Outbound sockets will `bind` to this address pub outbound_bind_addr: Option, + /// Outbound UDP sockets allow IP fragmentation + pub outbound_udp_allow_fragmentation: bool, /// Path to protect callback unix address, only for Android #[cfg(target_os = "android")] pub outbound_vpn_protect_path: Option, @@ -1480,6 +1490,7 @@ impl Config { outbound_user_cookie: None, outbound_bind_interface: None, outbound_bind_addr: None, + outbound_udp_allow_fragmentation: false, #[cfg(target_os = "android")] outbound_vpn_protect_path: None, @@ -1999,6 +2010,7 @@ impl Config { outbound_fwmark: config.outbound_fwmark, outbound_bind_addr, outbound_bind_interface: config.outbound_bind_interface.clone(), + outbound_udp_allow_fragmentation: config.outbound_udp_allow_fragmentation, }; nconfig.server.push(server_instance); @@ -2192,6 +2204,7 @@ impl Config { outbound_fwmark: config.outbound_fwmark, outbound_bind_addr, outbound_bind_interface: config.outbound_bind_interface.clone(), + outbound_udp_allow_fragmentation: config.outbound_udp_allow_fragmentation, }; if let Some(acl_path) = svr.acl { @@ -2222,6 +2235,10 @@ impl Config { server_instance.outbound_bind_interface = Some(outbound_bind_interface.clone()); } + if let Some(outbound_udp_allow_fragmentation) = svr.outbound_udp_allow_fragmentation { + server_instance.outbound_udp_allow_fragmentation = Some(outbound_udp_allow_fragmentation); + } + nconfig.server.push(server_instance); } } @@ -2387,6 +2404,10 @@ impl Config { // Bind device / interface nconfig.outbound_bind_interface = config.outbound_bind_interface; + if let Some(b) = config.outbound_udp_allow_fragmentation { + nconfig.outbound_udp_allow_fragmentation = b; + } + // Security if let Some(sec) = config.security { if let Some(replay_attack) = sec.replay_attack { @@ -3045,6 +3066,7 @@ impl fmt::Display for Config { outbound_fwmark: inst.outbound_fwmark, outbound_bind_addr: inst.outbound_bind_addr, outbound_bind_interface: inst.outbound_bind_interface.clone(), + outbound_udp_allow_fragmentation: inst.outbound_udp_allow_fragmentation, }); } @@ -3149,6 +3171,7 @@ impl fmt::Display for Config { jconf.outbound_bind_addr = self.outbound_bind_addr.map(|i| i.to_string()); jconf.outbound_bind_interface.clone_from(&self.outbound_bind_interface); + jconf.outbound_udp_allow_fragmentation = Some(self.outbound_udp_allow_fragmentation); // Security if self.security.replay_attack.policy != ReplayAttackPolicy::default() { diff --git a/crates/shadowsocks-service/src/manager/server.rs b/crates/shadowsocks-service/src/manager/server.rs index 42050c42dd10..06557f5ab260 100644 --- a/crates/shadowsocks-service/src/manager/server.rs +++ b/crates/shadowsocks-service/src/manager/server.rs @@ -388,6 +388,7 @@ impl Manager { outbound_fwmark: None, outbound_bind_addr: None, outbound_bind_interface: None, + outbound_udp_allow_fragmentation: None, }; let mut config = Config::new(ConfigType::Server); diff --git a/crates/shadowsocks-service/src/server/mod.rs b/crates/shadowsocks-service/src/server/mod.rs index c4a7d7d58466..c4382542a66e 100644 --- a/crates/shadowsocks-service/src/server/mod.rs +++ b/crates/shadowsocks-service/src/server/mod.rs @@ -66,6 +66,7 @@ pub async fn run(config: Config) -> io::Result<()> { bind_local_addr: config.outbound_bind_addr.map(|ip| SocketAddr::new(ip, 0)), bind_interface: config.outbound_bind_interface, + udp_allow_fragmentation: config.outbound_udp_allow_fragmentation, ..Default::default() }; @@ -120,6 +121,10 @@ pub async fn run(config: Config) -> io::Result<()> { connect_opts.bind_interface = Some(bind_interface); } + if let Some(udp_allow_fragmentation) = inst.outbound_udp_allow_fragmentation { + connect_opts.udp_allow_fragmentation = udp_allow_fragmentation; + } + server_builder.set_connect_opts(connect_opts); server_builder.set_accept_opts(accept_opts); diff --git a/crates/shadowsocks/src/net/option.rs b/crates/shadowsocks/src/net/option.rs index dffea6351f4b..743f98f32552 100644 --- a/crates/shadowsocks/src/net/option.rs +++ b/crates/shadowsocks/src/net/option.rs @@ -65,6 +65,9 @@ pub struct ConnectOpts { /// Outbound socket binds to interface pub bind_interface: Option, + /// Outbound UDP socket allows IP fragmentation + pub udp_allow_fragmentation: bool, + /// TCP options pub tcp: TcpSocketOpts, diff --git a/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs b/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs index 46d4ab5dc848..9b105098dd18 100644 --- a/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs +++ b/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs @@ -251,8 +251,10 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, _config: &ConnectO UdpSocket::from_std(socket.into())? }; - if let Err(err) = set_disable_ip_fragmentation(af, &socket) { - warn!("failed to disable IP fragmentation, error: {}", err); + if ! config.udp_allow_fragmentation { + if let Err(err) = set_disable_ip_fragmentation(af, &socket) { + warn!("failed to disable IP fragmentation, error: {}", err); + } } Ok(socket) diff --git a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs index b170bf1013d0..fa1fd4af7e1e 100644 --- a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs +++ b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs @@ -379,8 +379,10 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, config: &ConnectOp UdpSocket::from_std(socket.into())? }; - if let Err(err) = set_disable_ip_fragmentation(af, &socket) { - warn!("failed to disable IP fragmentation, error: {}", err); + if ! config.udp_allow_fragmentation { + if let Err(err) = set_disable_ip_fragmentation(af, &socket) { + warn!("failed to disable IP fragmentation, error: {}", err); + } } // Set IP_BOUND_IF for BSD-like diff --git a/crates/shadowsocks/src/net/sys/unix/linux/mod.rs b/crates/shadowsocks/src/net/sys/unix/linux/mod.rs index 4f52e4682e9d..d9c41c55b5f0 100644 --- a/crates/shadowsocks/src/net/sys/unix/linux/mod.rs +++ b/crates/shadowsocks/src/net/sys/unix/linux/mod.rs @@ -310,8 +310,10 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, config: &ConnectOp UdpSocket::from_std(socket.into())? }; - if let Err(err) = set_disable_ip_fragmentation(af, &socket) { - warn!("failed to disable IP fragmentation, error: {}", err); + if ! config.udp_allow_fragmentation { + if let Err(err) = set_disable_ip_fragmentation(af, &socket) { + warn!("failed to disable IP fragmentation, error: {}", err); + } } // Any traffic except localhost should be protected diff --git a/crates/shadowsocks/src/net/sys/windows/mod.rs b/crates/shadowsocks/src/net/sys/windows/mod.rs index a2ff2e4592ed..92c69f3c184a 100644 --- a/crates/shadowsocks/src/net/sys/windows/mod.rs +++ b/crates/shadowsocks/src/net/sys/windows/mod.rs @@ -498,9 +498,12 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, opts: &ConnectOpts socket.set_nonblocking(true)?; let socket = UdpSocket::from_std(socket.into())?; - if let Err(err) = set_disable_ip_fragmentation(af, &socket) { - warn!("failed to disable IP fragmentation, error: {}", err); + if ! opts.udp_allow_fragmentation { + if let Err(err) = set_disable_ip_fragmentation(af, &socket) { + warn!("failed to disable IP fragmentation, error: {}", err); + } } + disable_connection_reset(&socket)?; Ok(socket)