From 0e0edda77b4bb43ce49df0e0f487d10bde168d29 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Wed, 9 Feb 2022 02:59:26 +0800 Subject: [PATCH] support SO_USER_COOKIE on FreeBSD --- crates/shadowsocks-service/src/config.rs | 5 +++++ crates/shadowsocks/src/net/option.rs | 5 +++++ .../src/net/sys/unix/bsd/freebsd.rs | 18 ++++++++++++++++++ src/service/local.rs | 18 ++++++++++++++++++ src/service/manager.rs | 18 ++++++++++++++++++ src/service/server.rs | 18 ++++++++++++++++++ 6 files changed, 82 insertions(+) diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index f708621cce43..172ca076cde9 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -1021,6 +1021,9 @@ pub struct Config { /// Set `SO_MARK` socket option for outbound sockets #[cfg(any(target_os = "linux", target_os = "android"))] pub outbound_fwmark: Option, + /// Set `SO_USER_COOKIE` socket option for outbound sockets + #[cfg(target_os = "freebsd")] + pub outbound_user_cookie: Option, /// Set `SO_BINDTODEVICE` (Linux), `IP_BOUND_IF` (BSD), `IP_UNICAST_IF` (Windows) socket option for outbound sockets pub outbound_bind_interface: Option, /// Outbound sockets will `bind` to this address @@ -1146,6 +1149,8 @@ impl Config { #[cfg(any(target_os = "linux", target_os = "android"))] outbound_fwmark: None, + #[cfg(target_os = "freebsd")] + outbound_user_cookie: None, outbound_bind_interface: None, outbound_bind_addr: None, #[cfg(target_os = "android")] diff --git a/crates/shadowsocks/src/net/option.rs b/crates/shadowsocks/src/net/option.rs index 951f152edbe9..39091dc2475b 100644 --- a/crates/shadowsocks/src/net/option.rs +++ b/crates/shadowsocks/src/net/option.rs @@ -29,6 +29,11 @@ pub struct ConnectOpts { #[cfg(any(target_os = "linux", target_os = "android"))] pub fwmark: Option, + /// FreeBSD SO_USER_COOKIE + /// https://www.freebsd.org/cgi/man.cgi?query=setsockopt&sektion=2 + #[cfg(target_os = "freebsd")] + pub user_cookie: Option, + /// An IPC unix socket path for sending file descriptors to call `VpnService.protect` /// /// This is an [Android shadowsocks implementation](https://github.com/shadowsocks/shadowsocks-android) specific feature diff --git a/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs b/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs index 423f84c0ada8..a532b5a1017d 100644 --- a/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs +++ b/crates/shadowsocks/src/net/sys/unix/bsd/freebsd.rs @@ -35,6 +35,24 @@ impl TcpStream { SocketAddr::V6(..) => TcpSocket::new_v6()?, }; + // Set SO_USER_COOKIE for mark-based routing on FreeBSD + if let Some(user_cookie) = opts.user_cookie { + let ret = unsafe { + libc::setsockopt( + socket.as_raw_fd(), + libc::SOL_SOCKET, + libc::SO_USER_COOKIE, + &user_cookie as *const _ as *const _, + mem::size_of_val(&user_cookie) as libc::socklen_t, + ) + }; + if ret != 0 { + let err = io::Error::last_os_error(); + error!("set SO_USER_COOKIE error: {}", err); + return Err(err); + } + } + set_common_sockopt_for_connect(addr, &socket, opts)?; if !opts.tcp.fastopen { diff --git a/src/service/local.rs b/src/service/local.rs index f62b910d5a22..d03a6fc656e8 100644 --- a/src/service/local.rs +++ b/src/service/local.rs @@ -213,6 +213,17 @@ pub fn define_command_line_options(mut app: App<'_>) -> App<'_> { ); } + #[cfg(target_os = "freebsd")] + { + app = app.arg( + Arg::new("OUTBOUND_USER_COOKIE") + .long("outbound-user-cookie") + .takes_value(true) + .validator(validator::validate_u32) + .help("Set SO_USER_COOKIE option for outbound sockets"), + ); + } + #[cfg(feature = "local-redir")] { if RedirType::tcp_default() != RedirType::NotSupported { @@ -637,6 +648,13 @@ pub fn main(matches: &ArgMatches) { Err(err) => err.exit(), } + #[cfg(target_os = "freebsd")] + match matches.value_of_t::("OUTBOUND_USER_COOKIE") { + Ok(user_cookie) => config.outbound_user_cookie = Some(user_cookie), + Err(ref err) if err.kind == ClapErrorKind::ArgumentNotFound => {} + Err(err) => err.exit(), + } + match matches.value_of_t::("OUTBOUND_BIND_INTERFACE") { Ok(iface) => config.outbound_bind_interface = Some(iface), Err(ref err) if err.kind == ClapErrorKind::ArgumentNotFound => {} diff --git a/src/service/manager.rs b/src/service/manager.rs index 660e3dfc24c3..f243fe2a489a 100644 --- a/src/service/manager.rs +++ b/src/service/manager.rs @@ -176,6 +176,17 @@ pub fn define_command_line_options(mut app: App<'_>) -> App<'_> { ); } + #[cfg(target_os = "freebsd")] + { + app = app.arg( + Arg::new("OUTBOUND_USER_COOKIE") + .long("outbound-user-cookie") + .takes_value(true) + .validator(validator::validate_u32) + .help("Set SO_USER_COOKIE option for outbound sockets"), + ); + } + #[cfg(feature = "multi-threaded")] { app = app @@ -269,6 +280,13 @@ pub fn main(matches: &ArgMatches) { Err(err) => err.exit(), } + #[cfg(target_os = "freebsd")] + match matches.value_of_t::("OUTBOUND_USER_COOKIE") { + Ok(user_cookie) => config.outbound_user_cookie = Some(user_cookie), + Err(ref err) if err.kind == ClapErrorKind::ArgumentNotFound => {} + Err(err) => err.exit(), + } + match matches.value_of_t::("OUTBOUND_BIND_INTERFACE") { Ok(iface) => config.outbound_bind_interface = Some(iface), Err(ref err) if err.kind == ClapErrorKind::ArgumentNotFound => {} diff --git a/src/service/server.rs b/src/service/server.rs index 380fe1a1e93b..099ab9e29dc2 100644 --- a/src/service/server.rs +++ b/src/service/server.rs @@ -189,6 +189,17 @@ pub fn define_command_line_options(mut app: App<'_>) -> App<'_> { ); } + #[cfg(target_os = "freebsd")] + { + app = app.arg( + Arg::new("OUTBOUND_USER_COOKIE") + .long("outbound-user-cookie") + .takes_value(true) + .validator(validator::validate_u32) + .help("Set SO_USER_COOKIE option for outbound sockets"), + ); + } + #[cfg(feature = "multi-threaded")] { app = app @@ -331,6 +342,13 @@ pub fn main(matches: &ArgMatches) { Err(err) => err.exit(), } + #[cfg(target_os = "freebsd")] + match matches.value_of_t::("OUTBOUND_USER_COOKIE") { + Ok(user_cookie) => config.outbound_user_cookie = Some(user_cookie), + Err(ref err) if err.kind == ClapErrorKind::ArgumentNotFound => {} + Err(err) => err.exit(), + } + match matches.value_of_t::("OUTBOUND_BIND_INTERFACE") { Ok(iface) => config.outbound_bind_interface = Some(iface), Err(ref err) if err.kind == ClapErrorKind::ArgumentNotFound => {}