Skip to content

Commit

Permalink
add support for OpenBSD/pf
Browse files Browse the repository at this point in the history
  • Loading branch information
ge9 committed Jun 25, 2024
1 parent 1a83e0e commit 938d296
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 28 deletions.
2 changes: 1 addition & 1 deletion crates/shadowsocks-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ bson = { version = "2.10.0", optional = true }
shadowsocks = { version = "1.20.1", path = "../shadowsocks", default-features = false }

# Just for the ioctl call macro
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))'.dependencies]
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
nix = { version = "0.29", features = ["ioctl"] }

[target.'cfg(windows)'.dependencies]
Expand Down
27 changes: 27 additions & 0 deletions crates/shadowsocks-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ cfg_if! {
/// Document: <https://www.freebsd.org/doc/handbook/firewalls-pf.html>
#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos",
target_os = "ios"
))]
Expand Down Expand Up @@ -551,6 +552,30 @@ cfg_if! {
const AVAILABLE_TYPES: &[&str] = &[RedirType::PacketFilter.name(), RedirType::IpFirewall.name()];
AVAILABLE_TYPES
}
} else if #[cfg(target_os = "openbsd")] {
/// Default TCP transparent proxy solution on this platform
pub fn tcp_default() -> RedirType {
RedirType::PacketFilter
}

/// Available TCP transparent proxy types
#[doc(hidden)]
pub fn tcp_available_types() -> &'static [&'static str] {
const AVAILABLE_TYPES: &[&str] = &[RedirType::PacketFilter.name()];
AVAILABLE_TYPES
}

/// Default UDP transparent proxy solution on this platform
pub fn udp_default() -> RedirType {
RedirType::PacketFilter
}

/// Available UDP transparent proxy types
#[doc(hidden)]
pub const fn udp_available_types() -> &'static [&'static str] {
const AVAILABLE_TYPES: &[&str] = &[RedirType::PacketFilter.name()];
AVAILABLE_TYPES
}
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
/// Default TCP transparent proxy solution on this platform
pub fn tcp_default() -> RedirType {
Expand Down Expand Up @@ -621,6 +646,7 @@ cfg_if! {

#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos",
target_os = "ios"
))]
Expand Down Expand Up @@ -661,6 +687,7 @@ cfg_if! {

#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos",
target_os = "ios",
))]
Expand Down
2 changes: 1 addition & 1 deletion crates/shadowsocks-service/src/local/redir/sys/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ cfg_if! {
#[allow(dead_code, non_upper_case_globals, non_snake_case, non_camel_case_types)]
#[allow(clippy::useless_transmute, clippy::too_many_arguments, clippy::unnecessary_cast)]
mod pfvar;
} else if #[cfg(target_os = "freebsd")] {
} else if #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] {
#[path = "pfvar_bindgen_freebsd.rs"]
#[allow(dead_code, non_upper_case_globals, non_snake_case, non_camel_case_types)]
#[allow(clippy::useless_transmute, clippy::too_many_arguments, clippy::unnecessary_cast)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
impl TcpListenerRedirExt for TcpListener {
async fn bind_redir(ty: RedirType, addr: SocketAddr, accept_opts: AcceptOpts) -> io::Result<TcpListener> {
match ty {
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "macos", target_os = "ios"))]
RedirType::PacketFilter => {}

#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
Expand Down Expand Up @@ -108,6 +108,10 @@ impl TcpStreamRedirExt for TcpStream {

PF.natlook(&bind_addr, &peer_addr, Protocol::TCP)
}
#[cfg(target_os = "openbsd")] //in OpenBSD, we can get TCP destination address with getsockname()
RedirType::PacketFilter => {
self.local_addr()
}
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
RedirType::IpFirewall => {
// ## IPFW
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ cfg_if! {
pub use self::linux::*;
} else if #[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd"))] {
target_os = "freebsd",
target_os = "openbsd"))] {
mod bsd;
pub use self::bsd::*;
}
Expand Down
153 changes: 131 additions & 22 deletions crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/bsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,29 +188,30 @@ fn set_bindany(level: libc::c_int, socket: &Socket) -> io::Result<()> {
Ok(())
}

// #[cfg(target_os = "openbsd")]
// fn set_bindany(_level: libc::c_int, socket: &Socket) -> io::Result<()> {
// let fd = socket.as_raw_fd();
//
// let enable: libc::c_int = 1;
//
// // https://man.openbsd.org/getsockopt.2
// unsafe {
// let ret = libc::setsockopt(
// fd,
// libc::SOL_SOCKET,
// libc::SO_BINDANY,
// &enable as *const _ as *const _,
// mem::size_of_val(&enable) as libc::socklen_t,
// );
// if ret != 0 {
// return Err(Error::last_os_error());
// }
// }
//
// Ok(())
// }
#[cfg(target_os = "openbsd")]
fn set_bindany(_level: libc::c_int, socket: &Socket) -> io::Result<()> {
let fd = socket.as_raw_fd();

let enable: libc::c_int = 1;

// https://man.openbsd.org/getsockopt.2
unsafe {
let ret = libc::setsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_BINDANY,
&enable as *const _ as *const _,
mem::size_of_val(&enable) as libc::socklen_t,
);
if ret != 0 {
return Err(Error::last_os_error());
}
}

Ok(())
}

#[cfg(target_os = "freebsd")]
fn set_ip_origdstaddr(level: libc::c_int, socket: &Socket) -> io::Result<()> {
// https://www.freebsd.org/cgi/man.cgi?query=ip&sektion=4&manpath=FreeBSD+9.0-RELEASE

Expand Down Expand Up @@ -240,6 +241,52 @@ fn set_ip_origdstaddr(level: libc::c_int, socket: &Socket) -> io::Result<()> {
Ok(())
}

#[cfg(target_os = "openbsd")]
fn set_ip_origdstaddr(level: libc::c_int, socket: &Socket) -> io::Result<()> {
// https://man.openbsd.org/pf.conf
let fd = socket.as_raw_fd();

let enable: libc::c_int = 1;

let opt = match level {
libc::IPPROTO_IP => libc::IP_RECVDSTADDR,
libc::IPPROTO_IPV6 => libc::IPV6_RECVPKTINFO,
_ => unreachable!("level can only be IPPROTO_IP or IPPROTO_IPV6"),
};

unsafe {
let ret = libc::setsockopt(
fd,
level,
opt,
&enable as *const _ as *const _,
mem::size_of_val(&enable) as libc::socklen_t,
);
if ret != 0 {
return Err(Error::last_os_error());
}
}

let opt2 = match level {
libc::IPPROTO_IP => 33, //libc::IP_RECVDSTPORT,
libc::IPPROTO_IPV6 => 64, //libc::IPV6_RECVDSTPORT,
_ => unreachable!("level can only be IPPROTO_IP or IPPROTO_IPV6"),
};
unsafe {
let ret = libc::setsockopt(
fd,
level,
opt2,
&enable as *const _ as *const _,
mem::size_of_val(&enable) as libc::socklen_t,
);
if ret != 0 {
return Err(Error::last_os_error());
}
}
Ok(())
}

fn set_disable_ip_fragmentation(level: libc::c_int, socket: &Socket) -> io::Result<()> {
// https://www.freebsd.org/cgi/man.cgi?query=ip&sektion=4&manpath=FreeBSD+9.0-RELEASE

Expand Down Expand Up @@ -288,11 +335,13 @@ fn set_socket_before_bind(addr: &SocketAddr, socket: &Socket) -> io::Result<()>
set_ip_origdstaddr(level, socket)?;

// 3. disable IP fragmentation
#[cfg(not(target_os = "openbsd"))]//seems unsupported in OpenBSD
set_disable_ip_fragmentation(level, socket)?;

Ok(())
}

#[cfg(target_os = "freebsd")]
fn get_destination_addr(msg: &libc::msghdr) -> io::Result<SocketAddr> {
// https://www.freebsd.org/cgi/man.cgi?ip(4)
//
Expand Down Expand Up @@ -337,6 +386,66 @@ fn get_destination_addr(msg: &libc::msghdr) -> io::Result<SocketAddr> {
}
}

#[cfg(target_os = "openbsd")]
fn get_destination_addr(msg: &libc::msghdr) -> io::Result<SocketAddr> {
unsafe {
let (_, addr) = SockAddr::try_init(|dst_addr, dst_addr_len| {
let mut cmsg: *mut libc::cmsghdr = libc::CMSG_FIRSTHDR(msg);
while !cmsg.is_null() {
let rcmsg = &*cmsg;
match (rcmsg.cmsg_level, rcmsg.cmsg_type) {
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
let toaddr_in = &mut *(dst_addr as *mut libc::sockaddr_in);
ptr::copy_nonoverlapping(
libc::CMSG_DATA(cmsg),
&(*toaddr_in).sin_addr as *const _ as *mut _,
mem::size_of::<libc::in_addr>(),
);
toaddr_in.sin_family = libc::AF_INET as u8;
*dst_addr_len = mem::size_of::<libc::sockaddr_in>() as libc::socklen_t;
}
(libc::IPPROTO_IP, 33) => {//libc::IP_RECVDSTPORT
let toaddr_in = &mut *(dst_addr as *mut libc::sockaddr_in);
ptr::copy_nonoverlapping(
libc::CMSG_DATA(cmsg),
&(*toaddr_in).sin_port as *const _ as *mut _,
mem::size_of::<libc::in_port_t>(),
);
//assuming address comes first, and then port
return Ok(());
}
(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
let toaddr_in = &mut *(dst_addr as *mut libc::sockaddr_in6);
ptr::copy_nonoverlapping(
libc::CMSG_DATA(cmsg),
&(*toaddr_in).sin6_addr as *const _ as *mut _,
mem::size_of::<libc::in6_addr>(),
);
toaddr_in.sin6_family = libc::AF_INET6 as u8;
*dst_addr_len = mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t;
}
(libc::IPPROTO_IPV6, 64) => {//libc::IPV6_RECVDSTPORT
let toaddr_in = &mut *(dst_addr as *mut libc::sockaddr_in6);
ptr::copy_nonoverlapping(
libc::CMSG_DATA(cmsg),
&(*toaddr_in).sin6_port as *const _ as *mut _,
mem::size_of::<libc::in_port_t>(),
);
//assuming address comes first, and then port
return Ok(());
}
_ => {}
}
cmsg = libc::CMSG_NXTHDR(msg, cmsg);
}
let err = Error::new(ErrorKind::InvalidData, "missing destination address in msghdr");
Err(err)
})?;

Ok(addr.as_socket().expect("SocketAddr"))
}
}

fn recv_dest_from(socket: &UdpSocket, buf: &mut [u8]) -> io::Result<(usize, SocketAddr, SocketAddr)> {
unsafe {
let mut control_buf = [0u8; 64];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cfg_if! {
} else if #[cfg(target_os = "macos")] {
mod macos;
pub use self::macos::*;
} else if #[cfg(any(target_os = "freebsd"))] {
} else if #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] {
mod bsd;
pub use self::bsd::*;
} else {
Expand Down
3 changes: 2 additions & 1 deletion crates/shadowsocks-service/src/local/tun/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ cfg_if! {
target_os = "linux",
target_os = "android",
target_os = "windows",
target_os = "freebsd"))] {
target_os = "freebsd",
target_os = "openbsd"))] {
use tun2::{
create_as_async, AsyncDevice, Configuration as TunConfiguration, AbstractDevice, Error as TunError, Layer,
};
Expand Down

0 comments on commit 938d296

Please sign in to comment.