From 30dfd6ad2820e586b9364f558f631319a8ab1e13 Mon Sep 17 00:00:00 2001 From: Rusty Bee <145002912+rustybee42@users.noreply.github.com> Date: Thu, 21 Aug 2025 14:28:24 +0200 Subject: [PATCH 1/2] fix: Fall back to IPv4 sockets if IPv6 is unavailable Also fix broadcasting of UDP messages which before would abort on a send failure instead of just dumping the message to all known addresses. Now only debug logs and only errors if all sends failed. --- mgmtd/src/bee_msg.rs | 16 ++++++++-------- mgmtd/src/grpc.rs | 21 +++++++++++++++++---- mgmtd/src/lib.rs | 27 ++++++++++++++++++--------- shared/src/conn/outgoing.rs | 17 +++++++++++++---- 4 files changed, 56 insertions(+), 25 deletions(-) diff --git a/mgmtd/src/bee_msg.rs b/mgmtd/src/bee_msg.rs index b8379ef..0d2a542 100644 --- a/mgmtd/src/bee_msg.rs +++ b/mgmtd/src/bee_msg.rs @@ -196,8 +196,8 @@ pub async fn notify_nodes( ) { log::trace!("NOTIFICATION to {node_types:?}: {msg:?}"); - if let Err(err) = async { - for t in node_types { + for t in node_types { + if let Err(err) = async { let nodes = ctx .db .read_tx(move |tx| db::node::get_with_type(tx, *t)) @@ -206,12 +206,12 @@ pub async fn notify_nodes( ctx.conn .broadcast_datagram(nodes.into_iter().map(|e| e.uid), msg) .await?; - } - Ok(()) as Result<_> - } - .await - { - log::error!("Notification could not be sent to all nodes: {err:#}"); + Ok(()) as Result<_> + } + .await + { + log::error!("Notification could not be sent to all {t} nodes: {err:#}"); + } } } diff --git a/mgmtd/src/grpc.rs b/mgmtd/src/grpc.rs index 55ab919..8c69236 100644 --- a/mgmtd/src/grpc.rs +++ b/mgmtd/src/grpc.rs @@ -16,7 +16,7 @@ use sqlite::{TransactionExt, check_affected_rows}; use sqlite_check::sql; use std::fmt::Debug; use std::future::Future; -use std::net::SocketAddr; +use std::net::{SocketAddr, TcpListener}; use std::pin::Pin; use tonic::transport::{Identity, Server, ServerTlsConfig}; use tonic::{Code, Request, Response, Status}; @@ -181,13 +181,12 @@ pub(crate) fn serve(ctx: Context, mut shutdown: RunStateHandle) -> Result<()> { builder }; - let serve_addr = SocketAddr::new("::".parse()?, ctx.info.user_config.grpc_port); - + let ctx2 = ctx.clone(); let service = pm::management_server::ManagementServer::with_interceptor( ManagementService { ctx: ctx.clone() }, move |req: Request<()>| { // If authentication is enabled, require the secret passed with every request - if let Some(required_secret) = ctx.info.auth_secret { + if let Some(required_secret) = ctx2.info.auth_secret { let check = || -> Result<()> { let Some(request_secret) = req.metadata().get("auth-secret") else { bail!("Request requires authentication but no secret was provided") @@ -211,6 +210,20 @@ pub(crate) fn serve(ctx: Context, mut shutdown: RunStateHandle) -> Result<()> { }, ); + let mut serve_addr = SocketAddr::new("::".parse()?, ctx.info.user_config.grpc_port); + + // Test for IPv6 available, fall back to IPv4 sockets if not + match TcpListener::bind(serve_addr) { + Ok(_) => {} + Err(err) if err.raw_os_error() == Some(libc::EAFNOSUPPORT) => { + log::debug!("gRPC: IPv6 not available, falling back to IPv4 sockets"); + serve_addr = SocketAddr::new("0.0.0.0".parse()?, ctx.info.user_config.grpc_port); + } + Err(err) => { + anyhow::bail!(err); + } + } + log::info!("Serving gRPC requests on {serve_addr}"); tokio::spawn(async move { diff --git a/mgmtd/src/lib.rs b/mgmtd/src/lib.rs index 84743a5..d69c3b6 100644 --- a/mgmtd/src/lib.rs +++ b/mgmtd/src/lib.rs @@ -27,7 +27,7 @@ use sqlite::TransactionExt; use sqlite_check::sql; use std::collections::HashSet; use std::future::Future; -use std::net::SocketAddr; +use std::net::{SocketAddr, TcpListener}; use std::sync::Arc; use tokio::net::UdpSocket; use tokio::sync::mpsc; @@ -60,14 +60,22 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result {} + Err(err) if err.raw_os_error() == Some(libc::EAFNOSUPPORT) => { + log::debug!("BeeMsg: IPv6 not available, falling back to IPv4 sockets"); + beemsg_serve_addr = SocketAddr::new("0.0.0.0".parse()?, info.user_config.beemsg_port); + } + Err(err) => { + anyhow::bail!(err); + } + } + // UDP socket for in- and outgoing messages - let udp_socket = Arc::new( - UdpSocket::bind(SocketAddr::new( - "::0".parse()?, - info.user_config.beemsg_port, - )) - .await?, - ); + let udp_socket = Arc::new(UdpSocket::bind(beemsg_serve_addr).await?); // Node address store and connection pool let conn_pool = Pool::new( @@ -121,8 +129,9 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result Date: Wed, 3 Sep 2025 14:20:30 +0200 Subject: [PATCH 2/2] fix: Improve broadcast error logging --- shared/src/conn/outgoing.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/shared/src/conn/outgoing.rs b/shared/src/conn/outgoing.rs index 75e7b7e..c35bee4 100644 --- a/shared/src/conn/outgoing.rs +++ b/shared/src/conn/outgoing.rs @@ -185,6 +185,11 @@ impl Pool { Ok(()) } + /// Broadcasts a BeeMsg datagram to all given nodes using all their known addresses + /// + /// Logs errors if sending failed completely for a node, only fails if serialization fails. + /// Remember that this is UDP and thus no errors only means that the sending was successful, + /// not that the messages reached their destinations. pub async fn broadcast_datagram( &self, peers: impl IntoIterator, @@ -196,18 +201,26 @@ impl Pool { for node_uid in peers { let addrs = self.store.get_node_addrs(node_uid).unwrap_or_default(); - let mut sent = false; + if addrs.is_empty() { + log::error!( + "Failed to send datagram to node with uid {node_uid}: No known addresses" + ); + continue; + } + + let mut errs = vec![]; for addr in addrs.iter() { if let Err(err) = buf.send_to_socket(&self.udp_socket, addr).await { - log::debug!("Sending datagram to {addr} failed: {err}"); - continue; + log::debug!( + "Sending datagram to node with uid {node_uid} using {addr} failed: {err}" + ); + errs.push((addr, err)); } - sent = true; } - if !sent { + if errs.len() == addrs.len() { log::error!( - "Failed to send datagram to node with uid {node_uid}: Sending failed for all known addresses" + "Failed to send datagram to node with uid {node_uid} on all known addresses: {errs:?}" ); } }