Skip to content

Commit 7165c4f

Browse files
Tuetuopaycathay4t
authored andcommitted
Add ENOBUFS handling for unsolicited messages
This can happen when large burst of messages come all of a sudden, which happen very easily when routing protocols are involved (e.g. BGP). The current implementation incorrectly assumes that any failure to read from the socket is akin to the socket closed. This is not the case. This commit adds handling for this specific error by generating a `NetlinkPayload::Overrun(_)` message that users receive on their unsolicited message channel. Since this is just an additional message, there is no breaking change for existing users and they are free to ignore it if they do not want to handle it, or handle it by e.g. resyncing.
1 parent 9de99ce commit 7165c4f

File tree

1 file changed

+26
-6
lines changed

1 file changed

+26
-6
lines changed

src/framed.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
use bytes::BytesMut;
44
use std::{
5-
fmt::Debug,
6-
io,
7-
marker::PhantomData,
8-
pin::Pin,
9-
task::{Context, Poll},
5+
fmt::Debug, io, marker::PhantomData, mem::size_of, pin::Pin, task::{Context, Poll}
106
};
117

128
use futures::{Sink, Stream};
@@ -17,9 +13,13 @@ use crate::{
1713
sys::{AsyncSocket, SocketAddr},
1814
};
1915
use netlink_packet_core::{
20-
NetlinkDeserializable, NetlinkMessage, NetlinkSerializable,
16+
NetlinkDeserializable, NetlinkMessage, NetlinkSerializable, NetlinkHeader,
17+
NLMSG_OVERRUN, NetlinkPayload,
2118
};
2219

20+
/// Buffer overrun condition
21+
const ENOBUFS: i32 = 105;
22+
2323
pub struct NetlinkFramed<T, S, C> {
2424
socket: S,
2525
// see https://doc.rust-lang.org/nomicon/phantom-data.html
@@ -68,6 +68,26 @@ where
6868

6969
*in_addr = match ready!(socket.poll_recv_from(cx, reader)) {
7070
Ok(addr) => addr,
71+
// When receiving messages in multicast mode (i.e. we subscribed to
72+
// notifications), the kernel will not wait for us to read datagrams before
73+
// sending more. The receive buffer has a finite size, so once it is full (no
74+
// more message can fit in), new messages will be dropped and recv calls will
75+
// return `ENOBUFS`.
76+
// This needs to be handled for applications to resynchronize with the contents
77+
// of the kernel if necessary.
78+
// We don't need to do anything special:
79+
// - contents of the reader is still valid because we won't have partial messages
80+
// in there anyways (large enough buffer)
81+
// - contents of the socket's internal buffer is still valid because the kernel
82+
// won't put partial data in it
83+
Err(e) if e.raw_os_error() == Some(ENOBUFS) => {
84+
warn!("netlink socket buffer full");
85+
let mut hdr = NetlinkHeader::default();
86+
hdr.length = size_of::<NetlinkHeader>() as u32;
87+
hdr.message_type = NLMSG_OVERRUN;
88+
let msg = NetlinkMessage::new(hdr, NetlinkPayload::Overrun(Vec::new()));
89+
return Poll::Ready(Some((msg, SocketAddr::new(0, 0))));
90+
}
7191
Err(e) => {
7292
error!("failed to read from netlink socket: {:?}", e);
7393
return Poll::Ready(None);

0 commit comments

Comments
 (0)