Skip to content

Commit e1f8aaa

Browse files
committed
Fix build on FreeBSD, add CI test for FreeBSD, set IPV6_RECVPKTINFO on FreeBSD & macOS, set IP_RECVDSTADDR on FreeBSD and macOS only for IPv4, fix a warning about unused state: &UdpState, allow different types for the 3rd argument to sendmmsg()/recvmmsg()
1 parent 9f50319 commit e1f8aaa

File tree

4 files changed

+110
-20
lines changed

4 files changed

+110
-20
lines changed

.github/workflows/rust.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,30 @@ on:
88
- cron: "21 3 * * 5"
99

1010
jobs:
11+
test-freebsd:
12+
# see https://github.com/actions/runner/issues/385
13+
# use https://github.com/vmactions/freebsd-vm for now
14+
name: test on freebsd
15+
runs-on: macos-12
16+
steps:
17+
- uses: actions/checkout@v2
18+
- name: test on freebsd
19+
uses: vmactions/freebsd-vm@v0
20+
with:
21+
usesh: true
22+
mem: 4096
23+
copyback: false
24+
prepare: |
25+
pkg install -y curl
26+
curl https://sh.rustup.rs -sSf --output rustup.sh
27+
sh rustup.sh -y --profile minimal --default-toolchain stable
28+
echo "~~~~ rustc --version ~~~~"
29+
$HOME/.cargo/bin/rustc --version
30+
run: |
31+
freebsd-version
32+
$HOME/.cargo/bin/cargo build --all-targets
33+
$HOME/.cargo/bin/cargo test
34+
$HOME/.cargo/bin/cargo test --manifest-path fuzz/Cargo.toml
1135
test:
1236
strategy:
1337
matrix:

quinn-udp/src/unix.rs

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ impl Default for UdpSocketState {
6868

6969
fn init(io: SockRef<'_>) -> io::Result<()> {
7070
let mut cmsg_platform_space = 0;
71-
if cfg!(target_os = "linux") {
71+
if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") || cfg!(target_os = "macos") {
7272
cmsg_platform_space +=
7373
unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
7474
}
@@ -164,13 +164,20 @@ fn init(io: SockRef<'_>) -> io::Result<()> {
164164
if rc == -1 {
165165
return Err(io::Error::last_os_error());
166166
}
167-
167+
}
168+
}
169+
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
170+
// IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
171+
// macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS
172+
// macOS also supports IP_PKTINFO
173+
{
174+
if is_ipv4 {
168175
let on: libc::c_int = 1;
169176
let rc = unsafe {
170177
libc::setsockopt(
171178
io.as_raw_fd(),
172-
libc::IPPROTO_IPV6,
173-
libc::IPV6_RECVPKTINFO,
179+
libc::IPPROTO_IP,
180+
libc::IP_RECVDSTADDR,
174181
&on as *const _ as _,
175182
mem::size_of_val(&on) as _,
176183
)
@@ -180,7 +187,23 @@ fn init(io: SockRef<'_>) -> io::Result<()> {
180187
}
181188
}
182189
}
190+
191+
// IPV6_RECVPKTINFO is standardized
183192
if !is_ipv4 {
193+
let on: libc::c_int = 1;
194+
let rc = unsafe {
195+
libc::setsockopt(
196+
io.as_raw_fd(),
197+
libc::IPPROTO_IPV6,
198+
libc::IPV6_RECVPKTINFO,
199+
&on as *const _ as _,
200+
mem::size_of_val(&on) as _,
201+
)
202+
};
203+
if rc == -1 {
204+
return Err(io::Error::last_os_error());
205+
}
206+
184207
let on: libc::c_int = 1;
185208
let rc = unsafe {
186209
libc::setsockopt(
@@ -200,11 +223,24 @@ fn init(io: SockRef<'_>) -> io::Result<()> {
200223

201224
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
202225
fn send(
226+
#[allow(unused_variables)] // only used on Linux
203227
state: &UdpState,
204228
io: SockRef<'_>,
205229
last_send_error: &mut Instant,
206230
transmits: &[Transmit],
207231
) -> io::Result<usize> {
232+
#[allow(unused_mut)] // only mutable on FeeBSD
233+
let mut encode_src_ip = true;
234+
#[cfg(target_os = "freebsd")]
235+
{
236+
let addr = io.local_addr()?;
237+
let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
238+
if is_ipv4 {
239+
if let Some(socket) = addr.as_socket_ipv4() {
240+
encode_src_ip = socket.ip() == &Ipv4Addr::UNSPECIFIED;
241+
}
242+
}
243+
}
208244
let mut msgs: [libc::mmsghdr; BATCH_SIZE] = unsafe { mem::zeroed() };
209245
let mut iovecs: [libc::iovec; BATCH_SIZE] = unsafe { mem::zeroed() };
210246
let mut cmsgs = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
@@ -230,13 +266,13 @@ fn send(
230266
&mut msgs[i].msg_hdr,
231267
&mut iovecs[i],
232268
&mut cmsgs[i],
269+
encode_src_ip,
233270
);
234271
}
235272
let num_transmits = transmits.len().min(BATCH_SIZE);
236273

237274
loop {
238-
let n =
239-
unsafe { libc::sendmmsg(io.as_raw_fd(), msgs.as_mut_ptr(), num_transmits as u32, 0) };
275+
let n = unsafe { libc::sendmmsg(io.as_raw_fd(), msgs.as_mut_ptr(), num_transmits as _, 0) };
240276
if n == -1 {
241277
let e = io::Error::last_os_error();
242278
match e.kind() {
@@ -292,9 +328,18 @@ fn send(
292328
let mut iov: libc::iovec = unsafe { mem::zeroed() };
293329
let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
294330
let mut sent = 0;
331+
295332
while sent < transmits.len() {
296333
let addr = socket2::SockAddr::from(transmits[sent].destination);
297-
prepare_msg(&transmits[sent], &addr, &mut hdr, &mut iov, &mut ctrl);
334+
prepare_msg(
335+
&transmits[sent],
336+
&addr,
337+
&mut hdr,
338+
&mut iov,
339+
&mut ctrl,
340+
// Only tested on macOS
341+
cfg!(target_os = "macos"),
342+
);
298343
let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
299344
if n == -1 {
300345
let e = io::Error::last_os_error();
@@ -341,7 +386,7 @@ fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) ->
341386
libc::recvmmsg(
342387
io.as_raw_fd(),
343388
hdrs.as_mut_ptr(),
344-
bufs.len().min(BATCH_SIZE) as libc::c_uint,
389+
bufs.len().min(BATCH_SIZE) as _,
345390
0,
346391
ptr::null_mut(),
347392
)
@@ -401,6 +446,8 @@ fn prepare_msg(
401446
hdr: &mut libc::msghdr,
402447
iov: &mut libc::iovec,
403448
ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
449+
#[allow(unused_variables)] // only used on FreeBSD & macOS
450+
encode_src_ip: bool,
404451
) {
405452
iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
406453
iov.iov_len = transmit.contents.len();
@@ -432,9 +479,10 @@ fn prepare_msg(
432479
}
433480

434481
if let Some(ip) = &transmit.src_ip {
435-
if cfg!(target_os = "linux") {
436-
match ip {
437-
IpAddr::V4(v4) => {
482+
match ip {
483+
IpAddr::V4(v4) => {
484+
#[cfg(target_os = "linux")]
485+
{
438486
let pktinfo = libc::in_pktinfo {
439487
ipi_ifindex: 0,
440488
ipi_spec_dst: libc::in_addr {
@@ -444,16 +492,25 @@ fn prepare_msg(
444492
};
445493
encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
446494
}
447-
IpAddr::V6(v6) => {
448-
let pktinfo = libc::in6_pktinfo {
449-
ipi6_ifindex: 0,
450-
ipi6_addr: libc::in6_addr {
451-
s6_addr: v6.octets(),
452-
},
453-
};
454-
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
495+
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
496+
{
497+
if encode_src_ip {
498+
let addr = libc::in_addr {
499+
s_addr: u32::from_ne_bytes(v4.octets()),
500+
};
501+
encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
502+
}
455503
}
456504
}
505+
IpAddr::V6(v6) => {
506+
let pktinfo = libc::in6_pktinfo {
507+
ipi6_ifindex: 0,
508+
ipi6_addr: libc::in6_addr {
509+
s6_addr: v6.octets(),
510+
},
511+
};
512+
encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
513+
}
457514
}
458515
}
459516

@@ -504,12 +561,18 @@ fn decode_recv(
504561
ecn_bits = cmsg::decode::<libc::c_int>(cmsg) as u8;
505562
}
506563
},
564+
#[cfg(target_os = "linux")]
507565
(libc::IPPROTO_IP, libc::IP_PKTINFO) => {
508566
let pktinfo = unsafe { cmsg::decode::<libc::in_pktinfo>(cmsg) };
509567
dst_ip = Some(IpAddr::V4(Ipv4Addr::from(
510568
pktinfo.ipi_addr.s_addr.to_ne_bytes(),
511569
)));
512570
}
571+
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
572+
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
573+
let in_addr = unsafe { cmsg::decode::<libc::in_addr>(cmsg) };
574+
dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())));
575+
}
513576
(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
514577
let pktinfo = unsafe { cmsg::decode::<libc::in6_pktinfo>(cmsg) };
515578
dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr)));

quinn/src/connection.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ impl Connecting {
146146
/// Retrieving the local IP address is currently supported on the following
147147
/// platforms:
148148
/// - Linux
149+
/// - FreeBSD
150+
/// - macOS
149151
///
150152
/// On all non-supported platforms the local IP address will not be available,
151153
/// and the method will return `None`.

quinn/src/tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,8 @@ fn run_echo(args: EchoArgs) {
462462
// If `local_ip` gets available on additional platforms - which
463463
// requires modifying this test - please update the list of supported
464464
// platforms in the doc comments of the various `local_ip` functions.
465-
if cfg!(target_os = "linux") {
465+
if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") || cfg!(target_os = "macos")
466+
{
466467
let local_ip = incoming.local_ip().expect("Local IP must be available");
467468
assert!(local_ip.is_loopback());
468469
} else {

0 commit comments

Comments
 (0)