Skip to content

Commit

Permalink
Support UDP GSO and GRO on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
Gleb Pomykalov committed Apr 10, 2020
1 parent b5ee610 commit 81766f3
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 6 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ exclude = [
]

[dependencies]
libc = { version = "0.2.68", features = [ "extra_traits" ] }
#libc = { version = "0.2.68", features = [ "extra_traits" ] }
libc = { git = "https://github.com/rust-lang/libc.git", rev = "master", features = [ "extra_traits" ] }
bitflags = "1.1"
cfg-if = "0.1.10"
void = "1.0.2"
Expand Down
28 changes: 27 additions & 1 deletion src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,10 @@ pub enum ControlMessageOwned {
target_os = "openbsd",
))]
Ipv4RecvDstAddr(libc::in_addr),

#[cfg(target_os = "linux")]
UdpGroSegments(u16),

/// Catch-all variant for unimplemented cmsg types.
#[doc(hidden)]
Unknown(UnknownCmsg),
Expand Down Expand Up @@ -546,6 +550,11 @@ impl ControlMessageOwned {
let dl = ptr::read_unaligned(p as *const libc::in_addr);
ControlMessageOwned::Ipv4RecvDstAddr(dl)
},
#[cfg(target_os = "linux")]
(libc::SOL_UDP, libc::UDP_GRO) => {
let gso_size: u16 = ptr::read_unaligned(p as *const _);
ControlMessageOwned::UdpGroSegments(gso_size)
},
(_, _) => {
let sl = slice::from_raw_parts(p, len);
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(&sl[..]));
Expand Down Expand Up @@ -617,6 +626,9 @@ pub enum ControlMessage<'a> {
))]
AlgSetAeadAssoclen(&'a u32),

///Gso UDP
#[cfg(target_os = "linux")]
UdpGsoSegments(&'a u16),
}

// An opaque structure used to prevent cmsghdr from being a public type
Expand Down Expand Up @@ -676,6 +688,10 @@ impl<'a> ControlMessage<'a> {
ControlMessage::AlgSetAeadAssoclen(len) => {
len as *const _ as *const u8
},
#[cfg(target_os = "linux")]
ControlMessage::UdpGsoSegments(gso_size) => {
gso_size as *const _ as *const u8
},
};
unsafe {
ptr::copy_nonoverlapping(
Expand Down Expand Up @@ -708,6 +724,10 @@ impl<'a> ControlMessage<'a> {
ControlMessage::AlgSetAeadAssoclen(len) => {
mem::size_of_val(len)
},
#[cfg(target_os = "linux")]
ControlMessage::UdpGsoSegments(gso_size) => {
mem::size_of_val(gso_size)
},
}
}

Expand All @@ -719,7 +739,9 @@ impl<'a> ControlMessage<'a> {
ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG ,
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
#[cfg(target_os = "linux")]
ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
}
}

Expand All @@ -741,6 +763,10 @@ impl<'a> ControlMessage<'a> {
ControlMessage::AlgSetAeadAssoclen(_) => {
libc::ALG_SET_AEAD_ASSOCLEN
},
#[cfg(target_os = "linux")]
ControlMessage::UdpGsoSegments(_) => {
libc::UDP_SEGMENT
},
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,10 @@ sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
target_os = "openbsd",
))]
sockopt_impl!(Both, Ipv4RecvDstAddr, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);

#[cfg(target_os = "linux")]
sockopt_impl!(Both, UdpGsoSegment, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
#[cfg(target_os = "linux")]
sockopt_impl!(Both, UdpGroSegment, libc::IPPROTO_UDP, libc::UDP_GRO, bool);

#[cfg(any(target_os = "android", target_os = "linux"))]
#[derive(Copy, Clone, Debug)]
Expand Down
77 changes: 74 additions & 3 deletions test/sys/test_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ mod recvfrom {

const MSG: &'static [u8] = b"Hello, World!";

fn sendrecv<F>(rsock: RawFd, ssock: RawFd, f: F) -> Option<SockAddr>
fn sendrecv<F>(rsock: RawFd, ssock: RawFd, max_recv_once: Option<usize>, f: F) -> Option<SockAddr>
where F: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static
{
let mut buf: [u8; 13] = [0u8; 13];
Expand All @@ -185,6 +185,9 @@ mod recvfrom {

while l < std::mem::size_of_val(MSG) {
let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap();
if let Some(max) = max_recv_once {
assert!(len <= max);
}
from = from_;
l += len;
}
Expand All @@ -198,7 +201,7 @@ mod recvfrom {
let (fd2, fd1) = socketpair(AddressFamily::Unix, SockType::Stream,
None, SockFlag::empty()).unwrap();
// Ignore from for stream sockets
let _ = sendrecv(fd1, fd2, |s, m, flags| {
let _ = sendrecv(fd1, fd2, None, |s, m, flags| {
send(s, m, flags)
});
}
Expand All @@ -220,12 +223,80 @@ mod recvfrom {
SockFlag::empty(),
None,
).expect("send socket failed");
let from = sendrecv(rsock, ssock, move |s, m, flags| {
let from = sendrecv(rsock, ssock, None, move |s, m, flags| {
sendto(s, m, &sock_addr, flags)
});
// UDP sockets should set the from address
assert_eq!(AddressFamily::Inet, from.unwrap().family());
}

#[cfg(target_os = "linux")]
mod udp_offload {
use super::*;
use nix::sys::uio::IoVec;
use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment};

#[test]
pub fn gso() {
let std_sa = SocketAddr::from_str("127.0.0.1:6791").unwrap();
let inet_addr = InetAddr::from_std(&std_sa);
let sock_addr = SockAddr::new_inet(inet_addr);
let rsock = socket(AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None
).unwrap();

setsockopt(rsock, UdpGsoSegment, &2).expect("setsockopt UDP_SEGMENT failed");

bind(rsock, &sock_addr).unwrap();
let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
).expect("send socket failed");

let from = sendrecv(rsock, ssock, Some(2), move |s, m, flags| {
let iov = [IoVec::from_slice(m)];
let cmsg = ControlMessage::UdpGsoSegments(&2);
sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr))
});
// UDP sockets should set the from address
assert_eq!(AddressFamily::Inet, from.unwrap().family());
}

#[test]
pub fn gro() {
let std_sa = SocketAddr::from_str("127.0.0.1:6792").unwrap();
let inet_addr = InetAddr::from_std(&std_sa);
let sock_addr = SockAddr::new_inet(inet_addr);
let rsock = socket(AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None
).unwrap();

setsockopt(rsock, UdpGroSegment, &true).expect("setsockopt UDP_GRO failed");

bind(rsock, &sock_addr).unwrap();
let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
).expect("send socket failed");

let from = sendrecv(rsock, ssock, None, move |s, m, flags| {
let iov = [IoVec::from_slice(m)];
let cmsg = ControlMessage::UdpGsoSegments(&2);
sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr))
});

// UDP sockets should set the from address
assert_eq!(AddressFamily::Inet, from.unwrap().family());
}
}
}

// Test error handling of our recvmsg wrapper
Expand Down

0 comments on commit 81766f3

Please sign in to comment.