@@ -6,14 +6,16 @@ use std::{
66 os:: unix:: io:: AsRawFd ,
77 ptr,
88 task:: { Context , Poll } ,
9+ time:: Instant ,
910} ;
1011
1112use futures:: ready;
1213use lazy_static:: lazy_static;
1314use proto:: { EcnCodepoint , Transmit } ;
1415use tokio:: io:: unix:: AsyncFd ;
16+ use tracing:: warn;
1517
16- use super :: { cmsg, RecvMeta , UdpCapabilities } ;
18+ use super :: { cmsg, RecvMeta , UdpCapabilities , IO_ERROR_LOG_INTERVAL } ;
1719
1820#[ cfg( target_os = "freebsd" ) ]
1921type IpTosTy = libc:: c_uchar ;
@@ -27,26 +29,30 @@ type IpTosTy = libc::c_int;
2729#[ derive( Debug ) ]
2830pub struct UdpSocket {
2931 io : AsyncFd < mio:: net:: UdpSocket > ,
32+ last_send_error : Instant ,
3033}
3134
3235impl UdpSocket {
3336 pub fn from_std ( socket : std:: net:: UdpSocket ) -> io:: Result < UdpSocket > {
3437 socket. set_nonblocking ( true ) ?;
3538 let io = mio:: net:: UdpSocket :: from_std ( socket) ;
3639 init ( & io) ?;
40+ let now = Instant :: now ( ) ;
3741 Ok ( UdpSocket {
3842 io : AsyncFd :: new ( io) ?,
43+ last_send_error : now. checked_sub ( 2 * IO_ERROR_LOG_INTERVAL ) . unwrap_or ( now) ,
3944 } )
4045 }
4146
4247 pub fn poll_send (
43- & self ,
48+ & mut self ,
4449 cx : & mut Context ,
4550 transmits : & [ Transmit ] ,
4651 ) -> Poll < Result < usize , io:: Error > > {
4752 loop {
53+ let last_send_error = & mut self . last_send_error ;
4854 let mut guard = ready ! ( self . io. poll_write_ready( cx) ) ?;
49- if let Ok ( res) = guard. try_io ( |io| send ( io. get_ref ( ) , transmits) ) {
55+ if let Ok ( res) = guard. try_io ( |io| send ( io. get_ref ( ) , last_send_error , transmits) ) {
5056 return Poll :: Ready ( res) ;
5157 }
5258 }
@@ -184,7 +190,11 @@ fn init(io: &mio::net::UdpSocket) -> io::Result<()> {
184190}
185191
186192#[ cfg( not( any( target_os = "macos" , target_os = "ios" ) ) ) ]
187- fn send ( io : & mio:: net:: UdpSocket , transmits : & [ Transmit ] ) -> io:: Result < usize > {
193+ fn send (
194+ io : & mio:: net:: UdpSocket ,
195+ last_send_error : & mut Instant ,
196+ transmits : & [ Transmit ] ,
197+ ) -> io:: Result < usize > {
188198 let mut msgs: [ libc:: mmsghdr ; BATCH_SIZE ] = unsafe { mem:: zeroed ( ) } ;
189199 let mut iovecs: [ libc:: iovec ; BATCH_SIZE ] = unsafe { mem:: zeroed ( ) } ;
190200 let mut cmsgs = [ cmsg:: Aligned ( [ 0u8 ; CMSG_LEN ] ) ; BATCH_SIZE ] ;
@@ -224,23 +234,38 @@ fn send(io: &mio::net::UdpSocket, transmits: &[Transmit]) -> io::Result<usize> {
224234 // Retry the transmission
225235 continue ;
226236 }
227- io:: ErrorKind :: PermissionDenied => {
228- // Transmissions can fail with permission errors for example
229- // due to iptable rules. Those are not fatal errors, since the
230- // configuration can be dynamically changed.
231- // In this case we drop the outgoing packets and let higher
232- // layers retransmit if required.
233- return Ok ( num_transmits) ;
237+ io:: ErrorKind :: WouldBlock => return Err ( e) ,
238+ _ => {
239+ // Other errors are ignored, since they will ususally be handled
240+ // by higher level retransmits and timeouts.
241+ // - PermissionDenied errors have been observed due to iptable rules.
242+ // Those are not fatal errors, since the
243+ // configuration can be dynamically changed.
244+ // - Destination unreachable errors have been observed for other
245+ let now = Instant :: now ( ) ;
246+ if now. saturating_duration_since ( * last_send_error) > IO_ERROR_LOG_INTERVAL {
247+ * last_send_error = now;
248+ warn ! ( "sendmmsg error: {:?}, transmits: {:?}" , e, transmits) ;
249+ }
250+
251+ // The ERRORS section in https://man7.org/linux/man-pages/man2/sendmmsg.2.html
252+ // describes that errors will only be returned if no message could be transmitted
253+ // at all. Therefore drop the first (problematic) message,
254+ // and retry the remaining ones.
255+ return Ok ( num_transmits. min ( 1 ) ) ;
234256 }
235- _ => return Err ( e) ,
236257 }
237258 }
238259 return Ok ( n as usize ) ;
239260 }
240261}
241262
242263#[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
243- fn send ( io : & mio:: net:: UdpSocket , transmits : & [ Transmit ] ) -> io:: Result < usize > {
264+ fn send (
265+ io : & mio:: net:: UdpSocket ,
266+ last_send_error : & mut Instant ,
267+ transmits : & [ Transmit ] ,
268+ ) -> io:: Result < usize > {
244269 let mut hdr: libc:: msghdr = unsafe { mem:: zeroed ( ) } ;
245270 let mut iov: libc:: iovec = unsafe { mem:: zeroed ( ) } ;
246271 let mut ctrl = cmsg:: Aligned ( [ 0u8 ; CMSG_LEN ] ) ;
@@ -255,21 +280,22 @@ fn send(io: &mio::net::UdpSocket, transmits: &[Transmit]) -> io::Result<usize> {
255280 io:: ErrorKind :: Interrupted => {
256281 // Retry the transmission
257282 }
258- io:: ErrorKind :: PermissionDenied => {
259- // Transmissions can fail with permission errors for example
260- // due to iptable rules. Those are not fatal errors, since the
261- // configuration can be dynamically changed.
262- // In this case we drop the outgoing packets and let higher
263- // layers retransmit if required.
283+ io:: ErrorKind :: WouldBlock if sent != 0 => return Ok ( sent) ,
284+ io:: ErrorKind :: WouldBlock => return Err ( e) ,
285+ _ => {
286+ // Other errors are ignored, since they will ususally be handled
287+ // by higher level retransmits and timeouts.
288+ // - PermissionDenied errors have been observed due to iptable rules.
289+ // Those are not fatal errors, since the
290+ // configuration can be dynamically changed.
291+ // - Destination unreachable errors have been observed for other
292+ let now = Instant :: now ( ) ;
293+ if now. saturating_duration_since ( * last_send_error) > IO_ERROR_LOG_INTERVAL {
294+ * last_send_error = now;
295+ warn ! ( "sendmsg error: {:?}, transmit: {:?}" , e, & transmits[ sent] ) ;
296+ }
264297 sent += 1 ;
265298 }
266- _ if sent != 0 => {
267- // We need to report that some packets were sent in this case, so we rely on
268- // errors being either harmlessly transient (in the case of WouldBlock) or
269- // recurring on the next call.
270- return Ok ( sent) ;
271- }
272- _ => return Err ( e) ,
273299 }
274300 } else {
275301 sent += 1 ;
0 commit comments