Skip to content

Commit

Permalink
Allow IP fragmentation for outbound UDP sockets (#1790)
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-ryan authored Dec 1, 2024
1 parent 02c8451 commit 25485e5
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 8 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
23 changes: 23 additions & 0 deletions crates/shadowsocks-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ struct SSConfig {
#[serde(skip_serializing_if = "Option::is_none")]
outbound_bind_interface: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
outbound_udp_allow_fragmentation: Option<bool>,

#[serde(skip_serializing_if = "Option::is_none")]
security: Option<SSSecurityConfig>,

Expand Down Expand Up @@ -401,6 +404,9 @@ struct SSServerExtConfig {

#[serde(skip_serializing_if = "Option::is_none")]
outbound_bind_interface: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
outbound_udp_allow_fragmentation: Option<bool>,
}

#[cfg(feature = "local-online-config")]
Expand Down Expand Up @@ -1240,6 +1246,7 @@ pub struct ServerInstanceConfig {
pub outbound_fwmark: Option<u32>,
pub outbound_bind_addr: Option<IpAddr>,
pub outbound_bind_interface: Option<String>,
pub outbound_udp_allow_fragmentation: Option<bool>,
}

impl ServerInstanceConfig {
Expand All @@ -1252,6 +1259,7 @@ impl ServerInstanceConfig {
outbound_fwmark: None,
outbound_bind_addr: None,
outbound_bind_interface: None,
outbound_udp_allow_fragmentation: None,
}
}
}
Expand Down Expand Up @@ -1336,6 +1344,8 @@ pub struct Config {
pub outbound_bind_interface: Option<String>,
/// Outbound sockets will `bind` to this address
pub outbound_bind_addr: Option<IpAddr>,
/// 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<PathBuf>,
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
});
}

Expand Down Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions crates/shadowsocks-service/src/manager/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions crates/shadowsocks-service/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
};
Expand Down Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions crates/shadowsocks/src/net/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub struct ConnectOpts {
/// Outbound socket binds to interface
pub bind_interface: Option<String>,

/// Outbound UDP socket allows IP fragmentation
pub udp_allow_fragmentation: bool,

/// TCP options
pub tcp: TcpSocketOpts,

Expand Down
6 changes: 4 additions & 2 deletions crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions crates/shadowsocks/src/net/sys/unix/bsd/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions crates/shadowsocks/src/net/sys/unix/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions crates/shadowsocks/src/net/sys/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 25485e5

Please sign in to comment.