Skip to content

Commit 48c42a4

Browse files
zh-jqcursoragent
andcommitted
Enable FreeBSD/OpenBSD transparent proxy sockopts
Wire bindany and platform mark options (user_cookie / rtable) through listen, foreign bind, and misc configs; note the changes under vey-proxy 1.13.10 CHANGELOG. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent cd805cd commit 48c42a4

30 files changed

Lines changed: 498 additions & 88 deletions

File tree

lib/vey-json/src/value/net/tcp.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,18 @@ pub fn as_tcp_misc_sock_opts(v: &Value) -> anyhow::Result<TcpMiscSockOpts> {
134134
.context(format!("invalid u32 value for key {k}"))?;
135135
config.netfilter_mark = Some(mark);
136136
}
137+
#[cfg(target_os = "freebsd")]
138+
"user_cookie" => {
139+
let cookie = crate::value::as_u32(v)
140+
.context(format!("invalid u32 value for key {k}"))?;
141+
config.user_cookie = Some(cookie);
142+
}
143+
#[cfg(target_os = "openbsd")]
144+
"rtable" => {
145+
let rtable = crate::value::as_u32(v)
146+
.context(format!("invalid u32 value for key {k}"))?;
147+
config.rtable = Some(rtable);
148+
}
137149
_ => return Err(anyhow!("invalid key {k}")),
138150
}
139151
}
@@ -270,6 +282,20 @@ mod tests {
270282
let config = as_tcp_misc_sock_opts(&mark_json).unwrap();
271283
assert_eq!(config.netfilter_mark, Some(12345));
272284
}
285+
286+
#[cfg(target_os = "freebsd")]
287+
{
288+
let mark_json = json!({"user_cookie": 7});
289+
let config = as_tcp_misc_sock_opts(&mark_json).unwrap();
290+
assert_eq!(config.user_cookie, Some(7));
291+
}
292+
293+
#[cfg(target_os = "openbsd")]
294+
{
295+
let mark_json = json!({"rtable": 3});
296+
let config = as_tcp_misc_sock_opts(&mark_json).unwrap();
297+
assert_eq!(config.rtable, Some(3));
298+
}
273299
}
274300

275301
#[test]
@@ -295,8 +321,7 @@ mod tests {
295321
{
296322
assert!(as_tcp_misc_sock_opts(&json!({"congestion_control": ""})).is_err());
297323
assert!(
298-
as_tcp_misc_sock_opts(&json!({"congestion_control": "abcdefghijklmnop"}))
299-
.is_err()
324+
as_tcp_misc_sock_opts(&json!({"congestion_control": "abcdefghijklmnop"})).is_err()
300325
);
301326
}
302327
}

lib/vey-json/src/value/net/udp.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ pub fn as_udp_misc_sock_opts(v: &Value) -> anyhow::Result<UdpMiscSockOpts> {
4141
.context(format!("invalid u32 value for key {k}"))?;
4242
config.netfilter_mark = Some(mark);
4343
}
44+
#[cfg(target_os = "freebsd")]
45+
"user_cookie" => {
46+
let cookie = crate::value::as_u32(v)
47+
.context(format!("invalid u32 value for key {k}"))?;
48+
config.user_cookie = Some(cookie);
49+
}
50+
#[cfg(target_os = "openbsd")]
51+
"rtable" => {
52+
let rtable = crate::value::as_u32(v)
53+
.context(format!("invalid u32 value for key {k}"))?;
54+
config.rtable = Some(rtable);
55+
}
4456
_ => return Err(anyhow!("invalid key {k}")),
4557
}
4658
}
@@ -86,6 +98,20 @@ mod tests {
8698
let config = as_udp_misc_sock_opts(&mark_json).unwrap();
8799
assert_eq!(config.netfilter_mark, Some(99));
88100
}
101+
102+
#[cfg(target_os = "freebsd")]
103+
{
104+
let mark_json = json!({"user_cookie": 11});
105+
let config = as_udp_misc_sock_opts(&mark_json).unwrap();
106+
assert_eq!(config.user_cookie, Some(11));
107+
}
108+
109+
#[cfg(target_os = "openbsd")]
110+
{
111+
let mark_json = json!({"rtable": 5});
112+
let config = as_udp_misc_sock_opts(&mark_json).unwrap();
113+
assert_eq!(config.rtable, Some(5));
114+
}
89115
}
90116

91117
#[test]

lib/vey-slog-types/src/socket.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use slog::{Record, Serializer, Value};
88
use vey_socket::BindAddr;
99

1010
use crate::LtIpAddr;
11-
#[cfg(target_os = "linux")]
11+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
1212
use crate::LtSocketAddr;
1313

1414
pub struct LtBindAddr(pub BindAddr);
@@ -31,7 +31,7 @@ impl Value for LtBindAddr {
3131
target_os = "solaris"
3232
))]
3333
BindAddr::Interface(name) => serializer.emit_str(key, name.name()),
34-
#[cfg(target_os = "linux")]
34+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
3535
BindAddr::Foreign(addr) => LtSocketAddr(addr).serialize(record, key, serializer),
3636
}
3737
}

lib/vey-socket/src/bind.rs

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub enum BindAddr {
3737
target_os = "solaris"
3838
))]
3939
Interface(Interface),
40-
#[cfg(target_os = "linux")]
40+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
4141
Foreign(SocketAddr),
4242
}
4343

@@ -58,7 +58,7 @@ impl BindAddr {
5858
target_os = "solaris"
5959
))]
6060
BindAddr::Interface(_) => None,
61-
#[cfg(target_os = "linux")]
61+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
6262
BindAddr::Foreign(addr) => Some(addr.ip()),
6363
}
6464
}
@@ -96,7 +96,7 @@ impl BindAddr {
9696
AddressFamily::Ipv4 => socket.bind_device_by_index_v4(Some(iface.id())),
9797
AddressFamily::Ipv6 => socket.bind_device_by_index_v6(Some(iface.id())),
9898
},
99-
#[cfg(target_os = "linux")]
99+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
100100
BindAddr::Foreign(addr) => {
101101
if AddressFamily::from(addr) != peer_family {
102102
return Err(io::Error::new(
@@ -105,18 +105,12 @@ impl BindAddr {
105105
));
106106
}
107107
if addr.port() == 0 {
108+
#[cfg(target_os = "linux")]
108109
set_bind_address_no_port(socket, true)?;
109110
} else {
110111
socket.set_reuse_address(true)?;
111112
}
112-
match addr {
113-
SocketAddr::V4(_) => {
114-
socket.set_ip_transparent_v4(true)?;
115-
}
116-
SocketAddr::V6(_) => {
117-
crate::sockopt::set_ip_transparent_v6(socket, true)?;
118-
}
119-
}
113+
crate::sockopt::set_transparent(socket, peer_family)?;
120114
let addr: SockAddr = (*addr).into();
121115
socket.bind(&addr)
122116
}
@@ -153,7 +147,7 @@ impl BindAddr {
153147
AddressFamily::Ipv4 => socket.bind_device_by_index_v4(Some(iface.id())),
154148
AddressFamily::Ipv6 => socket.bind_device_by_index_v6(Some(iface.id())),
155149
},
156-
#[cfg(target_os = "linux")]
150+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
157151
BindAddr::Foreign(addr) => {
158152
if AddressFamily::from(addr) != peer_family {
159153
return Err(io::Error::new(
@@ -162,16 +156,10 @@ impl BindAddr {
162156
));
163157
}
164158
if addr.port() == 0 {
159+
#[cfg(target_os = "linux")]
165160
set_bind_address_no_port(socket, true)?;
166161
}
167-
match addr {
168-
SocketAddr::V4(_) => {
169-
socket.set_ip_transparent_v4(true)?;
170-
}
171-
SocketAddr::V6(_) => {
172-
crate::sockopt::set_ip_transparent_v6(socket, true)?;
173-
}
174-
}
162+
crate::sockopt::set_transparent(socket, peer_family)?;
175163
let addr: SockAddr = (*addr).into();
176164
socket.bind(&addr)
177165
}
@@ -204,22 +192,15 @@ impl BindAddr {
204192
IpAddr::V6(Ipv6Addr::UNSPECIFIED)
205193
}
206194
},
207-
#[cfg(target_os = "linux")]
195+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
208196
BindAddr::Foreign(addr) => {
209197
if AddressFamily::from(addr) != family {
210198
return Err(io::Error::new(
211199
io::ErrorKind::InvalidInput,
212200
"foreign bind addr has incorrect address family",
213201
));
214202
}
215-
match addr {
216-
SocketAddr::V4(_) => {
217-
socket.set_ip_transparent_v4(true)?;
218-
}
219-
SocketAddr::V6(_) => {
220-
crate::sockopt::set_ip_transparent_v6(socket, true)?;
221-
}
222-
}
203+
crate::sockopt::set_transparent(socket, family)?;
223204
let addr: SockAddr = (*addr).into();
224205
return socket.bind(&addr);
225206
}

lib/vey-socket/src/raw/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ impl RawSocket {
8989
if let Some(mark) = misc_opts.netfilter_mark {
9090
socket.set_mark(mark)?;
9191
}
92+
#[cfg(target_os = "freebsd")]
93+
if let Some(cookie) = misc_opts.user_cookie {
94+
crate::sockopt::set_user_cookie(socket, cookie)?;
95+
}
96+
#[cfg(target_os = "openbsd")]
97+
if let Some(rtable) = misc_opts.rtable {
98+
crate::sockopt::set_rtable(socket, rtable)?;
99+
}
92100
Ok(())
93101
}
94102

@@ -156,6 +164,14 @@ impl RawSocket {
156164
if let Some(mark) = misc_opts.netfilter_mark {
157165
socket.set_mark(mark)?;
158166
}
167+
#[cfg(target_os = "freebsd")]
168+
if let Some(cookie) = misc_opts.user_cookie {
169+
crate::sockopt::set_user_cookie(socket, cookie)?;
170+
}
171+
#[cfg(target_os = "openbsd")]
172+
if let Some(rtable) = misc_opts.rtable {
173+
crate::sockopt::set_rtable(socket, rtable)?;
174+
}
159175
Ok(())
160176
}
161177
}

lib/vey-socket/src/sockopt/unix/freebsd.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
/*
22
* SPDX-License-Identifier: Apache-2.0
33
* SPDX-FileCopyrightText: 2025 ByteDance and/or its affiliates.
4+
* SPDX-FileCopyrightText: 2026 VEY-OSS Developers.
45
*/
56

67
use std::io;
78
use std::os::unix::io::AsRawFd;
89

10+
use libc::c_int;
11+
912
pub(crate) fn set_tcp_reuseport_lb_numa_current_domain<T: AsRawFd>(fd: &T) -> io::Result<()> {
1013
const TCP_REUSPORT_LB_NUMA_CURDOM: i32 = -1;
1114

@@ -19,3 +22,39 @@ pub(crate) fn set_tcp_reuseport_lb_numa_current_domain<T: AsRawFd>(fd: &T) -> io
1922
Ok(())
2023
}
2124
}
25+
26+
pub(crate) fn set_ip_bindany_v4<T: AsRawFd>(fd: &T, enable: bool) -> io::Result<()> {
27+
unsafe {
28+
super::setsockopt(
29+
fd.as_raw_fd(),
30+
libc::IPPROTO_IP,
31+
libc::IP_BINDANY,
32+
enable as c_int,
33+
)?;
34+
Ok(())
35+
}
36+
}
37+
38+
pub(crate) fn set_ip_bindany_v6<T: AsRawFd>(fd: &T, enable: bool) -> io::Result<()> {
39+
unsafe {
40+
super::setsockopt(
41+
fd.as_raw_fd(),
42+
libc::IPPROTO_IPV6,
43+
libc::IPV6_BINDANY,
44+
enable as c_int,
45+
)?;
46+
Ok(())
47+
}
48+
}
49+
50+
pub(crate) fn set_user_cookie<T: AsRawFd>(fd: &T, cookie: u32) -> io::Result<()> {
51+
unsafe {
52+
super::setsockopt(
53+
fd.as_raw_fd(),
54+
libc::SOL_SOCKET,
55+
libc::SO_USER_COOKIE,
56+
cookie as c_int,
57+
)?;
58+
Ok(())
59+
}
60+
}

lib/vey-socket/src/sockopt/unix/linux.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ pub(crate) fn set_bind_address_no_port<T: AsRawFd>(fd: &T, enable: bool) -> io::
3737
}
3838
}
3939

40+
pub(crate) fn set_ip_transparent_v4<T: AsRawFd>(fd: &T, enable: bool) -> io::Result<()> {
41+
unsafe {
42+
super::setsockopt(
43+
fd.as_raw_fd(),
44+
libc::IPPROTO_IP,
45+
libc::IP_TRANSPARENT,
46+
enable as c_int,
47+
)?;
48+
Ok(())
49+
}
50+
}
51+
4052
pub(crate) fn set_ip_transparent_v6<T: AsRawFd>(fd: &T, enable: bool) -> io::Result<()> {
4153
unsafe {
4254
super::setsockopt(

lib/vey-socket/src/sockopt/unix/mod.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,28 @@ use std::{io, ptr};
1010

1111
use libc::{c_int, socklen_t};
1212

13+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
14+
use crate::util::AddressFamily;
15+
1316
#[cfg(any(target_os = "linux", target_os = "android"))]
1417
mod linux;
1518
#[cfg(any(target_os = "linux", target_os = "android"))]
1619
pub(crate) use linux::{
1720
attach_reuseport_ebpf, get_incoming_cpu, get_so_cookie, set_bind_address_no_port,
18-
set_incoming_cpu, set_ip_transparent_v6, set_tcp_quick_ack,
21+
set_incoming_cpu, set_ip_transparent_v4, set_ip_transparent_v6, set_tcp_quick_ack,
1922
};
2023

2124
#[cfg(target_os = "freebsd")]
2225
mod freebsd;
2326
#[cfg(target_os = "freebsd")]
24-
pub(crate) use freebsd::set_tcp_reuseport_lb_numa_current_domain;
27+
pub(crate) use freebsd::{
28+
set_ip_bindany_v4, set_ip_bindany_v6, set_tcp_reuseport_lb_numa_current_domain, set_user_cookie,
29+
};
30+
31+
#[cfg(target_os = "openbsd")]
32+
mod openbsd;
33+
#[cfg(target_os = "openbsd")]
34+
pub(crate) use openbsd::{set_bindany, set_rtable};
2535

2636
#[cfg(target_os = "solaris")]
2737
mod solaris;
@@ -33,6 +43,30 @@ mod illumos;
3343
#[cfg(target_os = "illumos")]
3444
pub(crate) use illumos::set_tcp_quick_ack;
3545

46+
/// Enable non-local bind for transparent / foreign bind.
47+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
48+
pub(crate) fn set_transparent<T: AsRawFd>(fd: &T, family: AddressFamily) -> io::Result<()> {
49+
#[cfg(target_os = "linux")]
50+
{
51+
match family {
52+
AddressFamily::Ipv6 => set_ip_transparent_v6(fd, true),
53+
AddressFamily::Ipv4 => set_ip_transparent_v4(fd, true),
54+
}
55+
}
56+
#[cfg(target_os = "freebsd")]
57+
{
58+
match family {
59+
AddressFamily::Ipv6 => set_ip_bindany_v6(fd, true),
60+
AddressFamily::Ipv4 => set_ip_bindany_v4(fd, true),
61+
}
62+
}
63+
#[cfg(target_os = "openbsd")]
64+
{
65+
let _ = family;
66+
set_bindany(fd, true)
67+
}
68+
}
69+
3670
unsafe fn setsockopt<T>(fd: c_int, level: c_int, name: c_int, value: T) -> io::Result<()>
3771
where
3872
T: Copy,

0 commit comments

Comments
 (0)