fix(network): recover IP address after a link flap / router reboot

The network module only populates the interface address from netlink
events (RTM_NEWADDR) or an explicit address dump. The interval timer
re-queries WiFi and bandwidth but never re-fetches the address, so the
module relies entirely on receiving the RTM_NEWADDR event.

Netlink multicast delivery is reliable unless the socket receive buffer
overflows, in which case the kernel drops notifications and reports
ENOBUFS. During a burst of link/address/route changes -- e.g. a router
reboot or a PPPoE redial -- this can drop the RTM_NEWADDR carrying the
interface's new IP (after the old one was removed by RTM_DELADDR). With
no overrun handling and no periodic resync, the address field stays
blank until Waybar is restarted (which re-dumps addresses).

Handle the overrun: when nl_recvmsgs_default reports ENOBUFS/NLE_NOMEM,
request a fresh link/address (and route, when auto-detecting) dump to
resynchronise, instead of silently continuing with lost state. Also
enlarge the event socket receive buffer to make overruns less likely in
the first place. The fix stays within the event thread, so it adds no
new locking or cross-thread socket access.

Fixes #5122.
This commit is contained in:
Alex
2026-07-05 11:09:46 +02:00
parent 5711b1f4ef
commit d518110e6f
+27
View File
@@ -183,6 +183,12 @@ void waybar::modules::Network::createEventSocket() {
if (nl_socket_set_nonblocking(ev_sock_)) {
throw std::runtime_error("Can't set non-blocking on network socket");
}
// Enlarge the socket receive buffer so that a burst of link/address/route
// change notifications (e.g. a router reboot or a PPPoE redial) is less likely
// to overflow it and make the kernel drop messages (ENOBUFS). Overruns are
// still handled in worker() by resynchronising the state, but a larger buffer
// avoids most of them. The kernel caps the request at net.core.rmem_max.
nl_socket_set_buffer_size(ev_sock_, 1024 * 1024, 0);
nl_socket_add_memberships(ev_sock_, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0);
if (!config_["interface"].isString()) {
nl_socket_add_memberships(ev_sock_, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0);
@@ -277,6 +283,27 @@ void waybar::modules::Network::worker() {
rc = 0;
break;
}
if (rc == -NLE_NOMEM || errno == ENOBUFS) {
// The kernel dropped multicast notifications because our receive
// buffer overflowed. This happens during a burst of
// link/address/route changes such as a router reboot or a PPPoE
// redial. We have lost track of the current state -- in
// particular the RTM_NEWADDR carrying the interface's new IP
// address may have been dropped -- so request a fresh dump to
// resynchronise. Without this the address (cleared by the
// preceding RTM_DELADDR) would stay blank until Waybar is
// restarted, because nothing else re-queries it (#5122).
spdlog::warn("network: netlink receive buffer overrun, resyncing state");
want_link_dump_ = true;
want_addr_dump_ = true;
if (!config_["interface"].isString()) {
want_route_dump_ = true;
}
askForStateDump();
// Keep draining; the next recv proceeds normally now that the
// overrun has been reported.
continue;
}
}
if (rc < 0) {
spdlog::error("nl_recvmsgs_default error: {}", nl_geterror(-rc));