Skip to content

Commit 5825f27

Browse files
committed
[#184] Supported freebsd and fuchsia TCP_FASTOPEN
1 parent e905aa2 commit 5825f27

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

src/relay/tcprelay/utils/tfo/bsd.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//! TCP Fast Open wrappers
2+
3+
use std::{
4+
io::{self, Error},
5+
mem,
6+
net::{self, IpAddr, SocketAddr},
7+
os::unix::io::AsRawFd,
8+
};
9+
10+
use libc;
11+
use log::error;
12+
use net2::TcpBuilder;
13+
use tokio::net::{TcpListener, TcpStream};
14+
15+
pub async fn bind_listener(addr: &SocketAddr) -> io::Result<TcpListener> {
16+
let listener = net::TcpListener::bind(addr)?;
17+
18+
let fd = listener.as_raw_fd();
19+
20+
unsafe {
21+
// TCP_FASTOPEN was supported since FreeBSD 12.0
22+
//
23+
// Example program:
24+
// https://people.freebsd.org/~pkelsey/tfo-tools/tfo-srv.c
25+
26+
let enable: libc::c_int = 1;
27+
28+
let ret = libc::setsockopt(
29+
fd,
30+
libc::IPPROTO_TCP,
31+
libc::TCP_FASTOPEN,
32+
&enable as *const _ as *const libc::c_void,
33+
mem::size_of_val(&enable) as libc::socklen_t,
34+
);
35+
36+
if ret != 0 {
37+
error!(
38+
"Failed to listen on {} with TFO enabled, supported after FreeBSD 12.0, ...",
39+
addr
40+
);
41+
42+
return Err(Error::last_os_error());
43+
}
44+
}
45+
46+
TcpListener::from_std(listener)
47+
}
48+
49+
pub async fn connect_stream(addr: &SocketAddr) -> io::Result<TcpStream> {
50+
let builder = match addr.ip() {
51+
IpAddr::V4(..) => TcpBuilder::new_v4()?,
52+
IpAddr::V6(..) => TcpBuilder::new_v6()?,
53+
};
54+
55+
// Build it first, to retrive the socket fd
56+
let stream = builder.to_tcp_stream()?;
57+
let sockfd = stream.as_raw_fd();
58+
59+
unsafe {
60+
// TCP_FASTOPEN was supported since FreeBSD 12.0
61+
//
62+
// Example program:
63+
// https://people.freebsd.org/~pkelsey/tfo-tools/tfo-client.c
64+
65+
let enable: libc::c_int = 1;
66+
67+
let ret = libc::setsockopt(
68+
sockfd,
69+
libc::IPPROTO_TCP,
70+
libc::TCP_FASTOPEN,
71+
&enable as *const _ as *const libc::c_void,
72+
mem::size_of_val(&enable) as libc::socklen_t,
73+
);
74+
75+
if ret != 0 {
76+
error!(
77+
"Failed to connect to {} with TFO enabled, supported after FreeBSD 12.0, ...",
78+
addr
79+
);
80+
81+
return Err(Error::last_os_error());
82+
}
83+
84+
let (saddr, saddr_len) = addr2raw(addr);
85+
86+
let empty_buf: [u8; 0] = [];
87+
88+
let ret = libc::sendto(
89+
sockfd,
90+
empty_buf.as_ptr() as *const libc::c_void,
91+
0,
92+
0, // Yes, FreeBSD doesn't need MSG_FASTOPEN
93+
saddr,
94+
saddr_len,
95+
);
96+
97+
if ret != 0 {
98+
return Err(Error::last_os_error());
99+
}
100+
}
101+
102+
TcpStream::from_std(stream)
103+
}
104+
105+
// Borrowed from net2
106+
fn addr2raw(addr: &SocketAddr) -> (*const libc::sockaddr, libc::socklen_t) {
107+
use std::mem;
108+
109+
match *addr {
110+
SocketAddr::V4(ref a) => (a as *const _ as *const _, mem::size_of_val(a) as libc::socklen_t),
111+
SocketAddr::V6(ref a) => (a as *const _ as *const _, mem::size_of_val(a) as libc::socklen_t),
112+
}
113+
}

src/relay/tcprelay/utils/tfo/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ cfg_if! {
1515
} else if #[cfg(target_os = "macos")] {
1616
#[path = "macos.rs"]
1717
mod sys;
18+
} else if #[cfg(any(target_os = "freebsd", target_os = "fuchsia"))] {
19+
#[path = "bsd.rs"]
20+
mod sys;
1821
} else {
1922
compile_error!("TFO is not supported on this platform yet");
2023
}

0 commit comments

Comments
 (0)