Skip to content

Commit 0e6673f

Browse files
committed
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 6c73fd1 commit 0e6673f

File tree

1 file changed

+35
-1
lines changed

1 file changed

+35
-1
lines changed

src/framed.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ use crate::{
1616
codecs::NetlinkMessageCodec,
1717
sys::{AsyncSocket, SocketAddr},
1818
};
19-
use netlink_packet_core::{NetlinkDeserializable, NetlinkMessage, NetlinkSerializable};
19+
use netlink_packet_core::{
20+
NetlinkDeserializable, NetlinkMessage, NetlinkSerializable, NetlinkHeader,
21+
NLMSG_OVERRUN, NetlinkPayload,
22+
};
23+
24+
/// Buffer overrun condition
25+
const ENOBUFS: i32 = 105;
2026

2127
pub struct NetlinkFramed<T, S, C> {
2228
socket: S,
@@ -63,6 +69,34 @@ where
6369

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

0 commit comments

Comments
 (0)