Skip to content

Commit 784ba6f

Browse files
committed
add support for OpenBSD/pf
1 parent 1a83e0e commit 784ba6f

File tree

10 files changed

+156
-28
lines changed

10 files changed

+156
-28
lines changed

crates/shadowsocks-service/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ bson = { version = "2.10.0", optional = true }
206206
shadowsocks = { version = "1.20.1", path = "../shadowsocks", default-features = false }
207207

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

212212
[target.'cfg(windows)'.dependencies]

crates/shadowsocks-service/src/config.rs

+27
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ cfg_if! {
487487
/// Document: <https://www.freebsd.org/doc/handbook/firewalls-pf.html>
488488
#[cfg(any(
489489
target_os = "freebsd",
490+
target_os = "openbsd",
490491
target_os = "macos",
491492
target_os = "ios"
492493
))]
@@ -551,6 +552,30 @@ cfg_if! {
551552
const AVAILABLE_TYPES: &[&str] = &[RedirType::PacketFilter.name(), RedirType::IpFirewall.name()];
552553
AVAILABLE_TYPES
553554
}
555+
} else if #[cfg(target_os = "openbsd")] {
556+
/// Default TCP transparent proxy solution on this platform
557+
pub fn tcp_default() -> RedirType {
558+
RedirType::PacketFilter
559+
}
560+
561+
/// Available TCP transparent proxy types
562+
#[doc(hidden)]
563+
pub fn tcp_available_types() -> &'static [&'static str] {
564+
const AVAILABLE_TYPES: &[&str] = &[RedirType::PacketFilter.name()];
565+
AVAILABLE_TYPES
566+
}
567+
568+
/// Default UDP transparent proxy solution on this platform
569+
pub fn udp_default() -> RedirType {
570+
RedirType::PacketFilter
571+
}
572+
573+
/// Available UDP transparent proxy types
574+
#[doc(hidden)]
575+
pub const fn udp_available_types() -> &'static [&'static str] {
576+
const AVAILABLE_TYPES: &[&str] = &[RedirType::PacketFilter.name()];
577+
AVAILABLE_TYPES
578+
}
554579
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
555580
/// Default TCP transparent proxy solution on this platform
556581
pub fn tcp_default() -> RedirType {
@@ -621,6 +646,7 @@ cfg_if! {
621646

622647
#[cfg(any(
623648
target_os = "freebsd",
649+
target_os = "openbsd",
624650
target_os = "macos",
625651
target_os = "ios"
626652
))]
@@ -661,6 +687,7 @@ cfg_if! {
661687

662688
#[cfg(any(
663689
target_os = "freebsd",
690+
target_os = "openbsd",
664691
target_os = "macos",
665692
target_os = "ios",
666693
))]

crates/shadowsocks-service/src/local/net/udp/association.rs

+2
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,8 @@ where
625625
if bypassed { "bypassed" } else { "proxied" },
626626
err
627627
);
628+
#[cfg(target_os = "openbsd")]
629+
let _ = self.respond_writer.send_to(self.peer_addr, &Address::SocketAddress(SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0,0,0,0)), 0)), data).await;
628630
} else {
629631
trace!(
630632
"udp relay {} <- {} ({}) with {} bytes",

crates/shadowsocks-service/src/local/redir/sys/unix/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ cfg_if! {
1515
#[allow(dead_code, non_upper_case_globals, non_snake_case, non_camel_case_types)]
1616
#[allow(clippy::useless_transmute, clippy::too_many_arguments, clippy::unnecessary_cast)]
1717
mod pfvar;
18-
} else if #[cfg(target_os = "freebsd")] {
18+
} else if #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] {
1919
#[path = "pfvar_bindgen_freebsd.rs"]
2020
#[allow(dead_code, non_upper_case_globals, non_snake_case, non_camel_case_types)]
2121
#[allow(clippy::useless_transmute, clippy::too_many_arguments, clippy::unnecessary_cast)]

crates/shadowsocks-service/src/local/redir/tcprelay/sys/unix/bsd.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::{
2121
impl TcpListenerRedirExt for TcpListener {
2222
async fn bind_redir(ty: RedirType, addr: SocketAddr, accept_opts: AcceptOpts) -> io::Result<TcpListener> {
2323
match ty {
24-
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
24+
#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "macos", target_os = "ios"))]
2525
RedirType::PacketFilter => {}
2626

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

109109
PF.natlook(&bind_addr, &peer_addr, Protocol::TCP)
110110
}
111+
#[cfg(target_os = "openbsd")] //in OpenBSD, we can get TCP destination address with getsockname()
112+
RedirType::PacketFilter => {
113+
self.local_addr()
114+
}
111115
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
112116
RedirType::IpFirewall => {
113117
// ## IPFW

crates/shadowsocks-service/src/local/redir/tcprelay/sys/unix/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ cfg_if! {
66
pub use self::linux::*;
77
} else if #[cfg(any(target_os = "macos",
88
target_os = "ios",
9-
target_os = "freebsd"))] {
9+
target_os = "freebsd",
10+
target_os = "openbsd"))] {
1011
mod bsd;
1112
pub use self::bsd::*;
1213
}

crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/bsd.rs

+115-22
Original file line numberDiff line numberDiff line change
@@ -188,29 +188,30 @@ fn set_bindany(level: libc::c_int, socket: &Socket) -> io::Result<()> {
188188
Ok(())
189189
}
190190

191-
// #[cfg(target_os = "openbsd")]
192-
// fn set_bindany(_level: libc::c_int, socket: &Socket) -> io::Result<()> {
193-
// let fd = socket.as_raw_fd();
194-
//
195-
// let enable: libc::c_int = 1;
196-
//
197-
// // https://man.openbsd.org/getsockopt.2
198-
// unsafe {
199-
// let ret = libc::setsockopt(
200-
// fd,
201-
// libc::SOL_SOCKET,
202-
// libc::SO_BINDANY,
203-
// &enable as *const _ as *const _,
204-
// mem::size_of_val(&enable) as libc::socklen_t,
205-
// );
206-
// if ret != 0 {
207-
// return Err(Error::last_os_error());
208-
// }
209-
// }
210-
//
211-
// Ok(())
212-
// }
191+
#[cfg(target_os = "openbsd")]
192+
fn set_bindany(_level: libc::c_int, socket: &Socket) -> io::Result<()> {
193+
let fd = socket.as_raw_fd();
194+
195+
let enable: libc::c_int = 1;
196+
197+
// https://man.openbsd.org/getsockopt.2
198+
unsafe {
199+
let ret = libc::setsockopt(
200+
fd,
201+
libc::SOL_SOCKET,
202+
libc::SO_BINDANY,
203+
&enable as *const _ as *const _,
204+
mem::size_of_val(&enable) as libc::socklen_t,
205+
);
206+
if ret != 0 {
207+
return Err(Error::last_os_error());
208+
}
209+
}
210+
211+
Ok(())
212+
}
213213

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

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

244+
#[cfg(target_os = "openbsd")]
245+
fn set_ip_origdstaddr(level: libc::c_int, socket: &Socket) -> io::Result<()> {
246+
// https://man.openbsd.org/pf.conf
247+
let fd = socket.as_raw_fd();
248+
249+
let enable: libc::c_int = 1;
250+
251+
let opt = match level {
252+
libc::IPPROTO_IP => libc::IP_RECVDSTADDR,
253+
libc::IPPROTO_IPV6 => libc::IPV6_RECVPKTINFO,
254+
_ => unreachable!("level can only be IPPROTO_IP or IPPROTO_IPV6"),
255+
};
256+
257+
unsafe {
258+
let ret = libc::setsockopt(
259+
fd,
260+
level,
261+
opt,
262+
&enable as *const _ as *const _,
263+
mem::size_of_val(&enable) as libc::socklen_t,
264+
);
265+
if ret != 0 {
266+
return Err(Error::last_os_error());
267+
}
268+
}
269+
270+
let opt2 = match level {
271+
libc::IPPROTO_IP => 33, //libc::IP_RECVDSTPORT,
272+
libc::IPPROTO_IPV6 => 64, //libc::IPV6_RECVDSTPORT,
273+
_ => unreachable!("level can only be IPPROTO_IP or IPPROTO_IPV6"),
274+
};
275+
unsafe {
276+
let ret = libc::setsockopt(
277+
fd,
278+
level,
279+
opt2,
280+
&enable as *const _ as *const _,
281+
mem::size_of_val(&enable) as libc::socklen_t,
282+
);
283+
if ret != 0 {
284+
return Err(Error::last_os_error());
285+
}
286+
}
287+
Ok(())
288+
}
289+
243290
fn set_disable_ip_fragmentation(level: libc::c_int, socket: &Socket) -> io::Result<()> {
244291
// https://www.freebsd.org/cgi/man.cgi?query=ip&sektion=4&manpath=FreeBSD+9.0-RELEASE
245292

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

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

293341
Ok(())
294342
}
295343

344+
#[cfg(target_os = "freebsd")]
296345
fn get_destination_addr(msg: &libc::msghdr) -> io::Result<SocketAddr> {
297346
// https://www.freebsd.org/cgi/man.cgi?ip(4)
298347
//
@@ -337,6 +386,50 @@ fn get_destination_addr(msg: &libc::msghdr) -> io::Result<SocketAddr> {
337386
}
338387
}
339388

389+
#[cfg(target_os = "openbsd")]
390+
fn get_destination_addr(msg: &libc::msghdr) -> io::Result<SocketAddr> {
391+
unsafe {
392+
let (_, addr) = SockAddr::try_init(|dst_addr, dst_addr_len| {
393+
let mut cmsg: *mut libc::cmsghdr = libc::CMSG_FIRSTHDR(msg);
394+
let toaddr_in = &mut *(dst_addr as *mut libc::sockaddr_in);
395+
while !cmsg.is_null() {
396+
let rcmsg = &*cmsg;
397+
match (rcmsg.cmsg_level, rcmsg.cmsg_type) {
398+
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
399+
ptr::copy_nonoverlapping(
400+
libc::CMSG_DATA(cmsg),
401+
&(*toaddr_in).sin_addr as *const _ as *mut _,
402+
mem::size_of::<libc::in_addr>(),
403+
);
404+
toaddr_in.sin_family = libc::AF_INET as u8;
405+
*dst_addr_len = mem::size_of::<libc::sockaddr_in>() as libc::socklen_t;
406+
}
407+
(libc::IPPROTO_IP, 33) => {//libc::IP_RECVDSTPORT
408+
ptr::copy_nonoverlapping(
409+
libc::CMSG_DATA(cmsg),
410+
&(*toaddr_in).sin_port as *const _ as *mut _,
411+
mem::size_of::<libc::in_port_t>(),
412+
);
413+
//assuming address comes first, and then port
414+
return Ok(());
415+
}
416+
//TODO: support IPv6
417+
// (libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO) => {
418+
// }
419+
// (libc::IPPROTO_IPV6, libc::IPV6_RECVDSTPORT) => {
420+
// }
421+
_ => {}
422+
}
423+
cmsg = libc::CMSG_NXTHDR(msg, cmsg);
424+
}
425+
let err = Error::new(ErrorKind::InvalidData, "missing destination address in msghdr");
426+
Err(err)
427+
})?;
428+
429+
Ok(addr.as_socket().expect("SocketAddr"))
430+
}
431+
}
432+
340433
fn recv_dest_from(socket: &UdpSocket, buf: &mut [u8]) -> io::Result<(usize, SocketAddr, SocketAddr)> {
341434
unsafe {
342435
let mut control_buf = [0u8; 64];

crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ cfg_if! {
77
} else if #[cfg(target_os = "macos")] {
88
mod macos;
99
pub use self::macos::*;
10-
} else if #[cfg(any(target_os = "freebsd"))] {
10+
} else if #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] {
1111
mod bsd;
1212
pub use self::bsd::*;
1313
} else {

crates/shadowsocks-service/src/local/tun/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ cfg_if! {
2727
target_os = "linux",
2828
target_os = "android",
2929
target_os = "windows",
30-
target_os = "freebsd"))] {
30+
target_os = "freebsd",
31+
target_os = "openbsd"))] {
3132
use tun2::{
3233
create_as_async, AsyncDevice, Configuration as TunConfiguration, AbstractDevice, Error as TunError, Layer,
3334
};

ktrace.out

4.19 MB
Binary file not shown.

0 commit comments

Comments
 (0)