mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-14 08:14:45 +00:00
disable IP fragmentation for UDP sockets
ref https://github.com/shadowsocks/shadowsocks-libev/commit/46382c23fac8fbabb62130ef49d3f0bf4c807b43 Setting IP_DONTFRAG on UDP sockets will reject packets that are too large to be fit into a MTU. This will help to prevent clients to send too large packets and probably improve performance.
This commit is contained in:
Generated
+2
-2
@@ -2000,7 +2000,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shadowsocks"
|
||||
version = "1.14.2"
|
||||
version = "1.14.3"
|
||||
dependencies = [
|
||||
"arc-swap 1.5.0",
|
||||
"async-trait",
|
||||
@@ -2055,7 +2055,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shadowsocks-rust"
|
||||
version = "1.14.3"
|
||||
version = "1.14.4"
|
||||
dependencies = [
|
||||
"build-time",
|
||||
"byte_string",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "shadowsocks-rust"
|
||||
version = "1.14.3"
|
||||
version = "1.14.4"
|
||||
authors = ["Shadowsocks Contributors"]
|
||||
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
|
||||
repository = "https://github.com/shadowsocks/shadowsocks-rust"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "shadowsocks"
|
||||
version = "1.14.2"
|
||||
version = "1.14.3"
|
||||
authors = ["Shadowsocks Contributors"]
|
||||
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
|
||||
repository = "https://github.com/shadowsocks/shadowsocks-rust"
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::{
|
||||
task::{self, Poll},
|
||||
};
|
||||
|
||||
use log::error;
|
||||
use log::{error, warn};
|
||||
use pin_project::pin_project;
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite, ReadBuf},
|
||||
@@ -166,6 +166,53 @@ pub fn set_tcp_fastopen<S: AsRawFd>(socket: &S) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disable IP fragmentation
|
||||
#[inline]
|
||||
pub fn set_disable_ip_fragmentation<S: AsRawFd>(af: AddrFamily, socket: &S) -> io::Result<()> {
|
||||
// https://www.freebsd.org/cgi/man.cgi?query=ip&sektion=4&manpath=FreeBSD+9.0-RELEASE
|
||||
|
||||
// sys/netinet/in.h
|
||||
const IP_DONTFRAG: libc::c_int = 67; // don't fragment packet
|
||||
|
||||
// sys/netinet6/in6.h
|
||||
const IPV6_DONTFRAG: libc::c_int = 62; // bool; disable IPv6 fragmentation
|
||||
|
||||
unsafe {
|
||||
match af {
|
||||
AddrFamily::Ipv4 => {
|
||||
let enable: i32 = 1;
|
||||
let ret = libc::setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
libc::IPPROTO_IP,
|
||||
IP_DONTFRAG,
|
||||
&enable as *const _ as *const _,
|
||||
mem::size_of_val(&enable) as libc::socklen_t,
|
||||
);
|
||||
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
AddrFamily::Ipv6 => {
|
||||
let enable: i32 = 1;
|
||||
let ret = libc::setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
libc::IPPROTO_IPV6,
|
||||
IPV6_DONTFRAG,
|
||||
&enable as *const _ as *const _,
|
||||
mem::size_of_val(&enable) as libc::socklen_t,
|
||||
);
|
||||
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a `UdpSocket` for connecting to `addr`
|
||||
#[inline(always)]
|
||||
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
|
||||
@@ -176,5 +223,10 @@ pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) ->
|
||||
(AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0),
|
||||
};
|
||||
|
||||
UdpSocket::bind(bind_addr).await
|
||||
let socket = UdpSocket::bind(bind_addr).await?;
|
||||
if let Err(err) = set_disable_ip_fragmentation(af, &socket) {
|
||||
warn!("failed to disable IP fragmentation, error: {}", err);
|
||||
}
|
||||
|
||||
Ok(socket)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
task::{self, Poll},
|
||||
};
|
||||
|
||||
use log::error;
|
||||
use log::{error, warn};
|
||||
use pin_project::pin_project;
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite, ReadBuf},
|
||||
@@ -202,6 +202,45 @@ fn set_ip_bound_if<S: AsRawFd>(socket: &S, addr: SocketAddr, iface: &str) -> io:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disable IP fragmentation
|
||||
#[inline]
|
||||
pub fn set_disable_ip_fragmentation<S: AsRawFd>(af: AddrFamily, socket: &S) -> io::Result<()> {
|
||||
unsafe {
|
||||
match af {
|
||||
AddrFamily::Ipv4 => {
|
||||
let enable: i32 = 1;
|
||||
let ret = libc::setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
libc::IPPROTO_IP,
|
||||
libc::IP_DONTFRAG,
|
||||
&enable as *const _ as *const _,
|
||||
mem::size_of_val(&enable) as libc::socklen_t,
|
||||
);
|
||||
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
AddrFamily::Ipv6 => {
|
||||
let enable: i32 = 1;
|
||||
let ret = libc::setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
libc::IPPROTO_IPV6,
|
||||
libc::IPV6_DONTFRAG,
|
||||
&enable as *const _ as *const _,
|
||||
mem::size_of_val(&enable) as libc::socklen_t,
|
||||
);
|
||||
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a `UdpSocket` for connecting to `addr`
|
||||
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
|
||||
let bind_addr = match (af, config.bind_local_addr) {
|
||||
@@ -212,6 +251,9 @@ pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) ->
|
||||
};
|
||||
|
||||
let socket = UdpSocket::bind(bind_addr).await?;
|
||||
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
|
||||
if let Some(ref iface) = config.bind_interface {
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use log::error;
|
||||
use log::{error, warn};
|
||||
use pin_project::pin_project;
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite, ReadBuf},
|
||||
@@ -197,6 +197,45 @@ pub fn set_tcp_fastopen<S: AsRawFd>(socket: &S) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disable IP fragmentation
|
||||
#[inline]
|
||||
pub fn set_disable_ip_fragmentation<S: AsRawFd>(af: AddrFamily, socket: &S) -> io::Result<()> {
|
||||
// For Linux, IP_MTU_DISCOVER should be enabled for both IPv4 and IPv6 sockets
|
||||
// https://man7.org/linux/man-pages/man7/ip.7.html
|
||||
|
||||
unsafe {
|
||||
let value: i32 = libc::IP_PMTUDISC_DO;
|
||||
let ret = libc::setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
libc::IPPROTO_IP,
|
||||
libc::IP_MTU_DISCOVER,
|
||||
&value as *const _ as *const _,
|
||||
mem::size_of_val(&value) as libc::socklen_t,
|
||||
);
|
||||
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
if af == AddrFamily::Ipv6 {
|
||||
let value: i32 = libc::IP_PMTUDISC_DO;
|
||||
let ret = libc::setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
libc::IPPROTO_IPV6,
|
||||
libc::IPV6_MTU_DISCOVER,
|
||||
&value as *const _ as *const _,
|
||||
mem::size_of_val(&value) as libc::socklen_t,
|
||||
);
|
||||
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a `UdpSocket` for connecting to `addr`
|
||||
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
|
||||
let bind_addr = match (af, config.bind_local_addr) {
|
||||
@@ -207,6 +246,9 @@ pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) ->
|
||||
};
|
||||
|
||||
let socket = UdpSocket::bind(bind_addr).await?;
|
||||
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
|
||||
// This is a workaround for VPNService
|
||||
|
||||
@@ -5,10 +5,11 @@ use std::{
|
||||
};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use log::warn;
|
||||
use socket2::{Domain, Protocol, Socket, TcpKeepalive, Type};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::net::{is_dual_stack_addr, sys::socket_bind_dual_stack, ConnectOpts};
|
||||
use crate::net::{is_dual_stack_addr, sys::socket_bind_dual_stack, AddrFamily, ConnectOpts};
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(any(target_os = "linux", target_os = "android"))] {
|
||||
@@ -36,16 +37,26 @@ pub mod uds;
|
||||
pub async fn create_inbound_udp_socket(addr: &SocketAddr, ipv6_only: bool) -> io::Result<UdpSocket> {
|
||||
let set_dual_stack = is_dual_stack_addr(addr);
|
||||
|
||||
if !set_dual_stack {
|
||||
UdpSocket::bind(addr).await
|
||||
let socket = if !set_dual_stack {
|
||||
UdpSocket::bind(addr).await?
|
||||
} else {
|
||||
let socket = Socket::new(Domain::for_address(*addr), Type::DGRAM, Some(Protocol::UDP))?;
|
||||
socket_bind_dual_stack(&socket, addr, ipv6_only)?;
|
||||
|
||||
// UdpSocket::from_std requires socket to be non-blocked
|
||||
socket.set_nonblocking(true)?;
|
||||
UdpSocket::from_std(socket.into())
|
||||
UdpSocket::from_std(socket.into())?
|
||||
};
|
||||
|
||||
let addr_family = match addr {
|
||||
SocketAddr::V4(..) => AddrFamily::Ipv4,
|
||||
SocketAddr::V6(..) => AddrFamily::Ipv6,
|
||||
};
|
||||
if let Err(err) = set_disable_ip_fragmentation(addr_family, &socket) {
|
||||
warn!("failed to disable IP fragmentation, error: {}", err);
|
||||
}
|
||||
|
||||
Ok(socket)
|
||||
}
|
||||
|
||||
pub fn set_common_sockopt_after_connect<S: AsRawFd>(stream: &S, opts: &ConnectOpts) -> io::Result<()> {
|
||||
|
||||
@@ -71,6 +71,12 @@ impl AsyncWrite for TcpStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable IP fragmentation
|
||||
#[inline]
|
||||
pub fn set_disable_ip_fragmentation<S: AsRawFd>(_af: AddrFamily, _socket: &S) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a `UdpSocket` for connecting to `addr`
|
||||
#[inline(always)]
|
||||
pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result<UdpSocket> {
|
||||
@@ -81,7 +87,10 @@ pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) ->
|
||||
(AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0),
|
||||
};
|
||||
|
||||
UdpSocket::bind(bind_addr).await
|
||||
let socket = UdpSocket::bind(bind_addr).await?;
|
||||
let _ = set_disable_ip_fragmentation(af, &socket);
|
||||
|
||||
Ok(socket)
|
||||
}
|
||||
|
||||
pub fn set_tcp_fastopen<S: AsRawFd>(_: &S) -> io::Result<()> {
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
task::{self, Poll},
|
||||
};
|
||||
|
||||
use log::error;
|
||||
use log::{error, warn};
|
||||
use pin_project::pin_project;
|
||||
use socket2::{Domain, Protocol, Socket, TcpKeepalive, Type};
|
||||
use tokio::{
|
||||
@@ -48,6 +48,10 @@ const TCP_FASTOPEN: DWORD = 15;
|
||||
// https://github.com/retep998/winapi-rs/pull/1007
|
||||
const IP_UNICAST_IF: DWORD = 31;
|
||||
|
||||
// ws2ipdef.h
|
||||
const IP_MTU_DISCOVER: DWORD = 71; // Set/get path MTU discover state.
|
||||
const IPV6_MTU_DISCOVER: DWORD = 71; // Set/get path MTU discover state.
|
||||
|
||||
/// A `TcpStream` that supports TFO (TCP Fast Open)
|
||||
#[pin_project(project = TcpStreamProj)]
|
||||
pub enum TcpStream {
|
||||
@@ -270,6 +274,61 @@ fn disable_connection_reset(socket: &UdpSocket) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disable IP fragmentation
|
||||
#[inline]
|
||||
pub fn set_disable_ip_fragmentation<S: AsRawSocket>(af: AddrFamily, socket: &S) -> io::Result<()> {
|
||||
// This is an enum PMTUD_STATE
|
||||
//
|
||||
// ```c
|
||||
// typedef enum _PMTUD_STATE {
|
||||
// IP_PMTUDISC_NOT_SET,
|
||||
// IP_PMTUDISC_DO,
|
||||
// IP_PMTUDISC_DONT,
|
||||
// IP_PMTUDISC_PROBE,
|
||||
// IP_PMTUDISC_MAX
|
||||
// } PMTUD_STATE, *PPMTUD_STATE;
|
||||
// ```
|
||||
const IP_PMTUDISC_DO: DWORD = 1;
|
||||
|
||||
let handle = socket.as_raw_socket() as SOCKET;
|
||||
|
||||
unsafe {
|
||||
// For Windows, IP_MTU_DISCOVER should be enabled for both IPv4 and IPv6 sockets
|
||||
// https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options
|
||||
let value: DWORD = IP_PMTUDISC_DO;
|
||||
let ret = setsockopt(
|
||||
handle,
|
||||
IPPROTO_IP as c_int,
|
||||
IP_MTU_DISCOVER as c_int,
|
||||
&value as *const _ as *const c_char,
|
||||
mem::size_of_val(&value) as c_int,
|
||||
);
|
||||
|
||||
if ret == SOCKET_ERROR {
|
||||
let err = io::Error::from_raw_os_error(WSAGetLastError());
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
if af == AddrFamily::Ipv6 {
|
||||
let value: DWORD = IP_PMTUDISC_DO;
|
||||
let ret = setsockopt(
|
||||
handle,
|
||||
IPPROTO_IPV6 as c_int,
|
||||
IPV6_MTU_DISCOVER as c_int,
|
||||
&value as *const _ as *const c_char,
|
||||
mem::size_of_val(&value) as c_int,
|
||||
);
|
||||
|
||||
if ret == SOCKET_ERROR {
|
||||
let err = io::Error::from_raw_os_error(WSAGetLastError());
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a `UdpSocket` binded to `addr`
|
||||
///
|
||||
/// It also disables `WSAECONNRESET` for UDP socket
|
||||
@@ -287,7 +346,15 @@ pub async fn create_inbound_udp_socket(addr: &SocketAddr, ipv6_only: bool) -> io
|
||||
UdpSocket::from_std(socket.into())?
|
||||
};
|
||||
|
||||
let addr_family = match addr {
|
||||
SocketAddr::V4(..) => AddrFamily::Ipv4,
|
||||
SocketAddr::V6(..) => AddrFamily::Ipv6,
|
||||
};
|
||||
if let Err(err) = set_disable_ip_fragmentation(addr_family, &socket) {
|
||||
warn!("failed to disable IP fragmentation, error: {}", err);
|
||||
}
|
||||
disable_connection_reset(&socket)?;
|
||||
|
||||
Ok(socket)
|
||||
}
|
||||
|
||||
@@ -302,6 +369,9 @@ pub async fn create_outbound_udp_socket(af: AddrFamily, opts: &ConnectOpts) -> i
|
||||
};
|
||||
|
||||
let socket = UdpSocket::bind(bind_addr).await?;
|
||||
if let Err(err) = set_disable_ip_fragmentation(af, &socket) {
|
||||
warn!("failed to disable IP fragmentation, error: {}", err);
|
||||
}
|
||||
disable_connection_reset(&socket)?;
|
||||
|
||||
if let Some(ref iface) = opts.bind_interface {
|
||||
|
||||
Reference in New Issue
Block a user