mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-04 18:29:29 +02:00
223 lines
6.8 KiB
Rust
223 lines
6.8 KiB
Rust
use std::{
|
|
io::IoSliceMut,
|
|
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket},
|
|
slice,
|
|
};
|
|
|
|
use quinn_udp::{EcnCodepoint, RecvMeta, Transmit, UdpSocketState};
|
|
use socket2::Socket;
|
|
|
|
#[test]
|
|
fn basic() {
|
|
let send = UdpSocket::bind("[::1]:0")
|
|
.or_else(|_| UdpSocket::bind("127.0.0.1:0"))
|
|
.unwrap();
|
|
let recv = UdpSocket::bind("[::1]:0")
|
|
.or_else(|_| UdpSocket::bind("127.0.0.1:0"))
|
|
.unwrap();
|
|
let dst_addr = recv.local_addr().unwrap();
|
|
test_send_recv(
|
|
&send.into(),
|
|
&recv.into(),
|
|
Transmit {
|
|
destination: dst_addr,
|
|
ecn: None,
|
|
contents: b"hello",
|
|
segment_size: None,
|
|
src_ip: None,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ecn_v6() {
|
|
let recv = socket2::Socket::new(
|
|
socket2::Domain::IPV6,
|
|
socket2::Type::DGRAM,
|
|
Some(socket2::Protocol::UDP),
|
|
)
|
|
.unwrap();
|
|
recv.set_only_v6(false).unwrap();
|
|
// We must use the unspecified address here, rather than a local address, to support dual-stack
|
|
// mode
|
|
recv.bind(&socket2::SockAddr::from(
|
|
"[::]:0".parse::<SocketAddr>().unwrap(),
|
|
))
|
|
.unwrap();
|
|
let recv_v6 = SocketAddr::V6(SocketAddrV6::new(
|
|
Ipv6Addr::LOCALHOST,
|
|
recv.local_addr().unwrap().as_socket().unwrap().port(),
|
|
0,
|
|
0,
|
|
));
|
|
let recv_v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, recv_v6.port()));
|
|
for (src, dst) in [("[::1]:0", recv_v6), ("127.0.0.1:0", recv_v4)] {
|
|
dbg!(src, dst);
|
|
let send = UdpSocket::bind(src).unwrap();
|
|
let send = Socket::from(send);
|
|
for codepoint in [EcnCodepoint::Ect0, EcnCodepoint::Ect1] {
|
|
test_send_recv(
|
|
&send,
|
|
&recv,
|
|
Transmit {
|
|
destination: dst,
|
|
ecn: Some(codepoint),
|
|
contents: b"hello",
|
|
segment_size: None,
|
|
src_ip: None,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn ecn_v4() {
|
|
let send = Socket::from(UdpSocket::bind("127.0.0.1:0").unwrap());
|
|
let recv = Socket::from(UdpSocket::bind("127.0.0.1:0").unwrap());
|
|
for codepoint in [EcnCodepoint::Ect0, EcnCodepoint::Ect1] {
|
|
test_send_recv(
|
|
&send,
|
|
&recv,
|
|
Transmit {
|
|
destination: recv.local_addr().unwrap().as_socket().unwrap(),
|
|
ecn: Some(codepoint),
|
|
contents: b"hello",
|
|
segment_size: None,
|
|
src_ip: None,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn ecn_v4_mapped_v6() {
|
|
let send = socket2::Socket::new(
|
|
socket2::Domain::IPV6,
|
|
socket2::Type::DGRAM,
|
|
Some(socket2::Protocol::UDP),
|
|
)
|
|
.unwrap();
|
|
send.set_only_v6(false).unwrap();
|
|
send.bind(&socket2::SockAddr::from(
|
|
"[::]:0".parse::<SocketAddr>().unwrap(),
|
|
))
|
|
.unwrap();
|
|
|
|
let recv = UdpSocket::bind("127.0.0.1:0").unwrap();
|
|
let recv = Socket::from(recv);
|
|
let recv_v4_mapped_v6 = SocketAddr::V6(SocketAddrV6::new(
|
|
Ipv4Addr::LOCALHOST.to_ipv6_mapped(),
|
|
recv.local_addr().unwrap().as_socket().unwrap().port(),
|
|
0,
|
|
0,
|
|
));
|
|
|
|
for codepoint in [EcnCodepoint::Ect0, EcnCodepoint::Ect1] {
|
|
test_send_recv(
|
|
&send,
|
|
&recv,
|
|
Transmit {
|
|
destination: recv_v4_mapped_v6,
|
|
ecn: Some(codepoint),
|
|
contents: b"hello",
|
|
segment_size: None,
|
|
src_ip: None,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[cfg_attr(not(any(target_os = "linux", target_os = "windows")), ignore)]
|
|
fn gso() {
|
|
let send = UdpSocket::bind("[::1]:0")
|
|
.or_else(|_| UdpSocket::bind("127.0.0.1:0"))
|
|
.unwrap();
|
|
let recv = UdpSocket::bind("[::1]:0")
|
|
.or_else(|_| UdpSocket::bind("127.0.0.1:0"))
|
|
.unwrap();
|
|
let max_segments = UdpSocketState::new((&send).into())
|
|
.unwrap()
|
|
.max_gso_segments();
|
|
let dst_addr = recv.local_addr().unwrap();
|
|
const SEGMENT_SIZE: usize = 128;
|
|
let msg = vec![0xAB; SEGMENT_SIZE * max_segments];
|
|
test_send_recv(
|
|
&send.into(),
|
|
&recv.into(),
|
|
Transmit {
|
|
destination: dst_addr,
|
|
ecn: None,
|
|
contents: &msg,
|
|
segment_size: Some(SEGMENT_SIZE),
|
|
src_ip: None,
|
|
},
|
|
);
|
|
}
|
|
|
|
fn test_send_recv(send: &Socket, recv: &Socket, transmit: Transmit) {
|
|
let send_state = UdpSocketState::new(send.into()).unwrap();
|
|
let recv_state = UdpSocketState::new(recv.into()).unwrap();
|
|
|
|
// Reverse non-blocking flag set by `UdpSocketState` to make the test non-racy
|
|
recv.set_nonblocking(false).unwrap();
|
|
|
|
send_state.send(send.into(), &transmit).unwrap();
|
|
|
|
let mut buf = [0; u16::MAX as usize];
|
|
let mut meta = RecvMeta::default();
|
|
let segment_size = transmit.segment_size.unwrap_or(transmit.contents.len());
|
|
let expected_datagrams = transmit.contents.len() / segment_size;
|
|
let mut datagrams = 0;
|
|
while datagrams < expected_datagrams {
|
|
let n = recv_state
|
|
.recv(
|
|
recv.into(),
|
|
&mut [IoSliceMut::new(&mut buf)],
|
|
slice::from_mut(&mut meta),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(n, 1);
|
|
let segments = meta.len / meta.stride;
|
|
for i in 0..segments {
|
|
assert_eq!(
|
|
&buf[(i * meta.stride)..((i + 1) * meta.stride)],
|
|
&transmit.contents
|
|
[(datagrams + i) * segment_size..(datagrams + i + 1) * segment_size]
|
|
);
|
|
}
|
|
datagrams += segments;
|
|
|
|
assert_eq!(
|
|
meta.addr.port(),
|
|
send.local_addr().unwrap().as_socket().unwrap().port()
|
|
);
|
|
let send_v6 = send.local_addr().unwrap().as_socket().unwrap().is_ipv6();
|
|
let recv_v6 = recv.local_addr().unwrap().as_socket().unwrap().is_ipv6();
|
|
let src = meta.addr.ip();
|
|
let dst = meta.dst_ip.unwrap();
|
|
for addr in [src, dst] {
|
|
match (send_v6, recv_v6) {
|
|
(_, false) => assert_eq!(addr, Ipv4Addr::LOCALHOST),
|
|
// Windows gives us real IPv4 addrs, whereas *nix use IPv6-mapped IPv4
|
|
// addrs. Canonicalize to IPv6-mapped for robustness.
|
|
(false, true) => {
|
|
assert_eq!(ip_to_v6_mapped(addr), Ipv4Addr::LOCALHOST.to_ipv6_mapped())
|
|
}
|
|
(true, true) => assert!(
|
|
addr == Ipv6Addr::LOCALHOST || addr == Ipv4Addr::LOCALHOST.to_ipv6_mapped()
|
|
),
|
|
}
|
|
}
|
|
assert_eq!(meta.ecn, transmit.ecn);
|
|
}
|
|
assert_eq!(datagrams, expected_datagrams);
|
|
}
|
|
|
|
fn ip_to_v6_mapped(x: IpAddr) -> IpAddr {
|
|
match x {
|
|
IpAddr::V4(x) => IpAddr::V6(x.to_ipv6_mapped()),
|
|
IpAddr::V6(_) => x,
|
|
}
|
|
}
|