diff --git a/README.md b/README.md index ed215362..5e2ae88f 100644 --- a/README.md +++ b/README.md @@ -480,7 +480,9 @@ Example configuration: "mode": "tcp_and_udp", // OPTIONAL. Authentication configuration file // Configuration file document could be found in the next section. - "socks5_auth_config_path": "/path/to/auth.json" + "socks5_auth_config_path": "/path/to/auth.json", + // OPTIONAL. Instance specific ACL + "acl": "/path/to/acl/file.acl", }, { // SOCKS5, SOCKS4/4a local server @@ -594,6 +596,9 @@ Example configuration: // The higher weight, the server may rank higher. "tcp_weight": 1.0, "udp_weight": 1.0, + + // OPTIONAL. Instance specific ACL + "acl": "/path/to/acl/file.acl", }, { // Same key as basic format "server" and "server_port" @@ -676,6 +681,16 @@ Example configuration: // Only valid for locals and servers listening on `::` "ipv6_only": false, + // Outbound socket options + // Linux Only (SO_MARK) + "outbound_fwmark": 255, + // FreeBSD only (SO_USER_COOKIE) + "outbound_user_cookie": 255, + // `SO_BINDTODEVICE` (Linux), `IP_BOUND_IF` (BSD), `IP_UNICAST_IF` (Windows) socket option for outbound sockets + "outbound_bind_interface": "eth1", + // Outbound socket bind() to this IP (choose a specific interface) + "outbound_bind_addr": "11.22.33.44", + // Balancer customization "balancer": { // MAX Round-Trip-Time (RTT) of servers diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index c46e4821..c01a37a2 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -66,14 +66,7 @@ use serde::{Deserialize, Serialize}; use shadowsocks::relay::socks5::Address; use shadowsocks::{ config::{ - ManagerAddr, - Mode, - ReplayAttackPolicy, - ServerAddr, - ServerConfig, - ServerUser, - ServerUserManager, - ServerWeight, + ManagerAddr, Mode, ReplayAttackPolicy, ServerAddr, ServerConfig, ServerUser, ServerUserManager, ServerWeight, }, crypto::CipherKind, plugin::PluginConfig, @@ -190,6 +183,16 @@ struct SSConfig { #[cfg(any(target_os = "linux", target_os = "android"))] outbound_fwmark: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg(target_os = "freebsd")] + outbound_user_cookie: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + outbound_bind_addr: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + outbound_bind_interface: Option, + #[serde(skip_serializing_if = "Option::is_none")] security: Option, @@ -1924,6 +1927,26 @@ impl Config { nconfig.outbound_fwmark = Some(fwmark); } + // SO_USER_COOKIE + #[cfg(target_os = "freebsd")] + if let Some(user_cookie) = config.outbound_user_cookie { + nconfig.outbound_user_cookie = Some(user_cookie); + } + + // Outbound bind() address + if let Some(bind_addr) = config.outbound_bind_addr { + match bind_addr.parse::() { + Ok(b) => nconfig.outbound_bind_addr = Some(b), + Err(..) => { + let err = Error::new(ErrorKind::Invalid, "invalid outbound_bind_addr", None); + return Err(err); + } + } + } + + // Bind device / interface + nconfig.outbound_bind_interface = config.outbound_bind_interface; + // Security if let Some(sec) = config.security { if let Some(replay_attack) = sec.replay_attack { @@ -2581,6 +2604,14 @@ impl fmt::Display for Config { jconf.outbound_fwmark = self.outbound_fwmark; } + #[cfg(target_os = "freebsd")] + { + jconf.outbound_user_cookie = self.outbound_user_cookie; + } + + jconf.outbound_bind_addr = self.outbound_bind_addr.map(|i| i.to_string()); + jconf.outbound_bind_interface = self.outbound_bind_interface.clone(); + // Security if self.security.replay_attack.policy != ReplayAttackPolicy::default() { jconf.security = Some(SSSecurityConfig {