Skip to content

Commit 81766f3

Browse files
author
Gleb Pomykalov
committed
Support UDP GSO and GRO on linux
1 parent b5ee610 commit 81766f3

File tree

4 files changed

+107
-6
lines changed

4 files changed

+107
-6
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ exclude = [
1616
]
1717

1818
[dependencies]
19-
libc = { version = "0.2.68", features = [ "extra_traits" ] }
19+
#libc = { version = "0.2.68", features = [ "extra_traits" ] }
20+
libc = { git = "https://github.com/rust-lang/libc.git", rev = "master", features = [ "extra_traits" ] }
2021
bitflags = "1.1"
2122
cfg-if = "0.1.10"
2223
void = "1.0.2"

src/sys/socket/mod.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,10 @@ pub enum ControlMessageOwned {
463463
target_os = "openbsd",
464464
))]
465465
Ipv4RecvDstAddr(libc::in_addr),
466+
467+
#[cfg(target_os = "linux")]
468+
UdpGroSegments(u16),
469+
466470
/// Catch-all variant for unimplemented cmsg types.
467471
#[doc(hidden)]
468472
Unknown(UnknownCmsg),
@@ -546,6 +550,11 @@ impl ControlMessageOwned {
546550
let dl = ptr::read_unaligned(p as *const libc::in_addr);
547551
ControlMessageOwned::Ipv4RecvDstAddr(dl)
548552
},
553+
#[cfg(target_os = "linux")]
554+
(libc::SOL_UDP, libc::UDP_GRO) => {
555+
let gso_size: u16 = ptr::read_unaligned(p as *const _);
556+
ControlMessageOwned::UdpGroSegments(gso_size)
557+
},
549558
(_, _) => {
550559
let sl = slice::from_raw_parts(p, len);
551560
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(&sl[..]));
@@ -617,6 +626,9 @@ pub enum ControlMessage<'a> {
617626
))]
618627
AlgSetAeadAssoclen(&'a u32),
619628

629+
///Gso UDP
630+
#[cfg(target_os = "linux")]
631+
UdpGsoSegments(&'a u16),
620632
}
621633

622634
// An opaque structure used to prevent cmsghdr from being a public type
@@ -676,6 +688,10 @@ impl<'a> ControlMessage<'a> {
676688
ControlMessage::AlgSetAeadAssoclen(len) => {
677689
len as *const _ as *const u8
678690
},
691+
#[cfg(target_os = "linux")]
692+
ControlMessage::UdpGsoSegments(gso_size) => {
693+
gso_size as *const _ as *const u8
694+
},
679695
};
680696
unsafe {
681697
ptr::copy_nonoverlapping(
@@ -708,6 +724,10 @@ impl<'a> ControlMessage<'a> {
708724
ControlMessage::AlgSetAeadAssoclen(len) => {
709725
mem::size_of_val(len)
710726
},
727+
#[cfg(target_os = "linux")]
728+
ControlMessage::UdpGsoSegments(gso_size) => {
729+
mem::size_of_val(gso_size)
730+
},
711731
}
712732
}
713733

@@ -719,7 +739,9 @@ impl<'a> ControlMessage<'a> {
719739
ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
720740
#[cfg(any(target_os = "android", target_os = "linux"))]
721741
ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
722-
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG ,
742+
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
743+
#[cfg(target_os = "linux")]
744+
ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
723745
}
724746
}
725747

@@ -741,6 +763,10 @@ impl<'a> ControlMessage<'a> {
741763
ControlMessage::AlgSetAeadAssoclen(_) => {
742764
libc::ALG_SET_AEAD_ASSOCLEN
743765
},
766+
#[cfg(target_os = "linux")]
767+
ControlMessage::UdpGsoSegments(_) => {
768+
libc::UDP_SEGMENT
769+
},
744770
}
745771
}
746772

src/sys/socket/sockopt.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,10 @@ sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
305305
target_os = "openbsd",
306306
))]
307307
sockopt_impl!(Both, Ipv4RecvDstAddr, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
308-
308+
#[cfg(target_os = "linux")]
309+
sockopt_impl!(Both, UdpGsoSegment, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
310+
#[cfg(target_os = "linux")]
311+
sockopt_impl!(Both, UdpGroSegment, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
309312

310313
#[cfg(any(target_os = "android", target_os = "linux"))]
311314
#[derive(Copy, Clone, Debug)]

test/sys/test_socket.rs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ mod recvfrom {
169169

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

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

186186
while l < std::mem::size_of_val(MSG) {
187187
let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap();
188+
if let Some(max) = max_recv_once {
189+
assert!(len <= max);
190+
}
188191
from = from_;
189192
l += len;
190193
}
@@ -198,7 +201,7 @@ mod recvfrom {
198201
let (fd2, fd1) = socketpair(AddressFamily::Unix, SockType::Stream,
199202
None, SockFlag::empty()).unwrap();
200203
// Ignore from for stream sockets
201-
let _ = sendrecv(fd1, fd2, |s, m, flags| {
204+
let _ = sendrecv(fd1, fd2, None, |s, m, flags| {
202205
send(s, m, flags)
203206
});
204207
}
@@ -220,12 +223,80 @@ mod recvfrom {
220223
SockFlag::empty(),
221224
None,
222225
).expect("send socket failed");
223-
let from = sendrecv(rsock, ssock, move |s, m, flags| {
226+
let from = sendrecv(rsock, ssock, None, move |s, m, flags| {
224227
sendto(s, m, &sock_addr, flags)
225228
});
226229
// UDP sockets should set the from address
227230
assert_eq!(AddressFamily::Inet, from.unwrap().family());
228231
}
232+
233+
#[cfg(target_os = "linux")]
234+
mod udp_offload {
235+
use super::*;
236+
use nix::sys::uio::IoVec;
237+
use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment};
238+
239+
#[test]
240+
pub fn gso() {
241+
let std_sa = SocketAddr::from_str("127.0.0.1:6791").unwrap();
242+
let inet_addr = InetAddr::from_std(&std_sa);
243+
let sock_addr = SockAddr::new_inet(inet_addr);
244+
let rsock = socket(AddressFamily::Inet,
245+
SockType::Datagram,
246+
SockFlag::empty(),
247+
None
248+
).unwrap();
249+
250+
setsockopt(rsock, UdpGsoSegment, &2).expect("setsockopt UDP_SEGMENT failed");
251+
252+
bind(rsock, &sock_addr).unwrap();
253+
let ssock = socket(
254+
AddressFamily::Inet,
255+
SockType::Datagram,
256+
SockFlag::empty(),
257+
None,
258+
).expect("send socket failed");
259+
260+
let from = sendrecv(rsock, ssock, Some(2), move |s, m, flags| {
261+
let iov = [IoVec::from_slice(m)];
262+
let cmsg = ControlMessage::UdpGsoSegments(&2);
263+
sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr))
264+
});
265+
// UDP sockets should set the from address
266+
assert_eq!(AddressFamily::Inet, from.unwrap().family());
267+
}
268+
269+
#[test]
270+
pub fn gro() {
271+
let std_sa = SocketAddr::from_str("127.0.0.1:6792").unwrap();
272+
let inet_addr = InetAddr::from_std(&std_sa);
273+
let sock_addr = SockAddr::new_inet(inet_addr);
274+
let rsock = socket(AddressFamily::Inet,
275+
SockType::Datagram,
276+
SockFlag::empty(),
277+
None
278+
).unwrap();
279+
280+
setsockopt(rsock, UdpGroSegment, &true).expect("setsockopt UDP_GRO failed");
281+
282+
bind(rsock, &sock_addr).unwrap();
283+
let ssock = socket(
284+
AddressFamily::Inet,
285+
SockType::Datagram,
286+
SockFlag::empty(),
287+
None,
288+
).expect("send socket failed");
289+
290+
let from = sendrecv(rsock, ssock, None, move |s, m, flags| {
291+
let iov = [IoVec::from_slice(m)];
292+
let cmsg = ControlMessage::UdpGsoSegments(&2);
293+
sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr))
294+
});
295+
296+
// UDP sockets should set the from address
297+
assert_eq!(AddressFamily::Inet, from.unwrap().family());
298+
}
299+
}
229300
}
230301

231302
// Test error handling of our recvmsg wrapper

0 commit comments

Comments
 (0)