Skip to content

Commit e1e923a

Browse files
authored
Add --ipv6-disable setting (#30)
feat: add --ipv6-disable setting This disables ipv6 completely, overriding any --interfaces settings. It removes ipv6 interfaces from the managements reported nics, lets listeners use ipv4 sockets and ignores other nodes ipv6 nics on outgoing communication. All of the above now also applies when ipv6 is automatically disabled.
1 parent ad9585e commit e1e923a

File tree

7 files changed

+61
-17
lines changed

7 files changed

+61
-17
lines changed

mgmtd/assets/beegfs-mgmtd.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,9 @@
6262
#
6363
# interfaces = ["*"]
6464

65-
# Prefers an interfaces ipv6 addresses over ipv4.
66-
# By default, ipv4 addresses are preferred. If the interface filter is given, the interface
67-
# order has higher priority than the address family, which is sorted per interface.
68-
# interfaces_prefer_ipv6 = false
65+
66+
# Force disable IPv6.
67+
# ipv6_disable = false,
6968

7069
# Maximum number of outgoing connections per node.
7170
# connection-limit = 12

mgmtd/src/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@ generate_structs! {
232232
#[arg(value_parser = nic::NicFilter::parse)]
233233
interfaces: Vec<NicFilter> = vec![],
234234

235+
/// Force disable IPv6.
236+
#[arg(long)]
237+
#[arg(num_args = 0..=1, default_missing_value = "true")]
238+
ipv6_disable: bool = false,
239+
235240
/// Maximum number of outgoing BeeMsg connections per node. [default: 12]
236241
#[arg(long)]
237242
#[arg(value_name = "LIMIT")]

mgmtd/src/grpc.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use sqlite::{TransactionExt, check_affected_rows};
1616
use sqlite_check::sql;
1717
use std::fmt::Debug;
1818
use std::future::Future;
19-
use std::net::SocketAddr;
19+
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
2020
use std::pin::Pin;
2121
use tonic::transport::{Identity, Server, ServerTlsConfig};
2222
use tonic::{Code, Request, Response, Status};
@@ -210,7 +210,14 @@ pub(crate) fn serve(ctx: Context, mut shutdown: RunStateHandle) -> Result<()> {
210210
},
211211
);
212212

213-
let serve_addr = shared::nic::select_bind_addr(ctx.info.user_config.grpc_port);
213+
let serve_addr = SocketAddr::new(
214+
if ctx.info.use_ipv6 {
215+
Ipv6Addr::UNSPECIFIED.into()
216+
} else {
217+
Ipv4Addr::UNSPECIFIED.into()
218+
},
219+
ctx.info.user_config.grpc_port,
220+
);
214221

215222
log::info!("Serving gRPC requests on {serve_addr}");
216223

mgmtd/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use sqlite::TransactionExt;
2727
use sqlite_check::sql;
2828
use std::collections::HashSet;
2929
use std::future::Future;
30+
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
3031
use std::sync::Arc;
3132
use tokio::net::UdpSocket;
3233
use tokio::sync::mpsc;
@@ -39,6 +40,7 @@ pub struct StaticInfo {
3940
pub user_config: Config,
4041
pub auth_secret: Option<AuthSecret>,
4142
pub network_addrs: Vec<Nic>,
43+
pub use_ipv6: bool,
4244
}
4345

4446
/// Starts the management service.
@@ -59,7 +61,14 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result<RunCont
5961
// Static configuration which doesn't change at runtime
6062
let info = Box::leak(Box::new(info));
6163

62-
let beemsg_serve_addr = shared::nic::select_bind_addr(info.user_config.beemsg_port);
64+
let beemsg_serve_addr = SocketAddr::new(
65+
if info.use_ipv6 {
66+
Ipv6Addr::UNSPECIFIED.into()
67+
} else {
68+
Ipv4Addr::UNSPECIFIED.into()
69+
},
70+
info.user_config.beemsg_port,
71+
);
6372

6473
// UDP socket for in- and outgoing messages
6574
let udp_socket = Arc::new(UdpSocket::bind(beemsg_serve_addr).await?);
@@ -69,6 +78,7 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result<RunCont
6978
udp_socket.clone(),
7079
info.user_config.connection_limit,
7180
info.auth_secret,
81+
info.use_ipv6,
7282
);
7383

7484
let mut db = sqlite::Connections::new(info.user_config.db_file.as_path());

mgmtd/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use mgmtd::db::{self};
55
use mgmtd::license::LicenseVerifier;
66
use mgmtd::{StaticInfo, start};
77
use shared::journald_logger;
8+
use shared::nic::check_ipv6;
89
use shared::types::AuthSecret;
910
use std::backtrace::{Backtrace, BacktraceStatus};
1011
use std::fmt::Write;
@@ -97,7 +98,8 @@ doc.beegfs.io.",
9798
None
9899
};
99100

100-
let network_addrs = shared::nic::query_nics(&user_config.interfaces)?;
101+
let use_ipv6 = check_ipv6(user_config.beemsg_port, !user_config.ipv6_disable);
102+
let network_addrs = shared::nic::query_nics(&user_config.interfaces, use_ipv6)?;
101103

102104
// Configure the tokio runtime
103105
let rt = tokio::runtime::Builder::new_multi_thread()
@@ -136,6 +138,7 @@ doc.beegfs.io.",
136138
// Start the actual daemon
137139
let run = start(
138140
StaticInfo {
141+
use_ipv6,
139142
user_config,
140143
auth_secret,
141144
network_addrs,

shared/src/conn/outgoing.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct Pool {
2626
store: Store,
2727
udp_socket: Arc<UdpSocket>,
2828
auth_secret: Option<AuthSecret>,
29+
use_ipv6: bool,
2930
}
3031

3132
impl Pool {
@@ -34,11 +35,13 @@ impl Pool {
3435
udp_socket: Arc<UdpSocket>,
3536
connection_limit: usize,
3637
auth_secret: Option<AuthSecret>,
38+
use_ipv6: bool,
3739
) -> Self {
3840
Self {
3941
store: Store::new(connection_limit),
4042
auth_secret,
4143
udp_socket,
44+
use_ipv6,
4245
}
4346
}
4447

@@ -118,6 +121,10 @@ impl Pool {
118121
log::debug!("Connecting new stream to {node_uid:?}");
119122

120123
for addr in addrs.iter() {
124+
if addr.is_ipv6() && !self.use_ipv6 {
125+
continue;
126+
}
127+
121128
match Stream::connect_tcp(addr).await {
122129
Ok(stream) => {
123130
let mut stream = StoredStream::from_stream(stream, permit);
@@ -210,6 +217,10 @@ impl Pool {
210217

211218
let mut errs = vec![];
212219
for addr in addrs.iter() {
220+
if addr.is_ipv6() && !self.use_ipv6 {
221+
continue;
222+
}
223+
213224
if let Err(err) = buf.send_to_socket(&self.udp_socket, addr).await {
214225
log::debug!(
215226
"Sending datagram to node with uid {node_uid} using {addr} failed: {err}"

shared/src/nic.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::types::NicType;
22
use anyhow::{Result, anyhow};
33
use serde::Deserializer;
44
use serde::de::{Unexpected, Visitor};
5-
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
5+
use std::net::{IpAddr, Ipv6Addr};
66
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
77
use std::str::FromStr;
88

@@ -187,11 +187,15 @@ impl Ord for Nic {
187187
///
188188
/// Only interfaces matching one of the given names in `filter` will be returned, unless the list
189189
/// is empty.
190-
pub fn query_nics(filter: &[NicFilter]) -> Result<Vec<Nic>> {
190+
pub fn query_nics(filter: &[NicFilter], use_ipv6: bool) -> Result<Vec<Nic>> {
191191
let mut filtered_nics = vec![];
192192

193193
for interface in pnet_datalink::interfaces() {
194194
for ip in interface.ips {
195+
if !use_ipv6 && ip.is_ipv6() {
196+
continue;
197+
}
198+
195199
if let Some(priority) = nic_priority(filter, &interface.name, &ip.ip()) {
196200
filtered_nics.push(Nic {
197201
name: interface.name.clone(),
@@ -208,17 +212,22 @@ pub fn query_nics(filter: &[NicFilter]) -> Result<Vec<Nic>> {
208212
Ok(filtered_nics)
209213
}
210214

211-
/// Selects address to bind to for listening: Checks if IPv6 sockets are available on this host
215+
/// Checks if IPv6 sockets are available on this host
212216
/// according to our rules: IPv6 must be enabled during boot and at runtime, and IPv6 sockets must
213-
/// be dual stack. Then it returns `::` (IPv6), otherwise `0.0.0.0` (IPv4).
214-
pub fn select_bind_addr(port: u16) -> SocketAddr {
217+
/// be dual stack.
218+
pub fn check_ipv6(port: u16, use_ipv6: bool) -> bool {
219+
if !use_ipv6 {
220+
log::info!("IPv6 is disabled by the configuration, falling back to IPv4 sockets");
221+
return false;
222+
}
223+
215224
// SAFETY: Any data used in the libc calls is local only
216225
unsafe {
217226
// Check if IPv6 socket can be created
218227
let sock = libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0);
219228
if sock < 0 {
220229
log::info!("IPv6 is unavailable on this host, falling back to IPv4 sockets");
221-
return SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port);
230+
return false;
222231
}
223232
// Make sure the socket is closed on drop
224233
let sock = OwnedFd::from_raw_fd(sock);
@@ -244,7 +253,7 @@ pub fn select_bind_addr(port: u16) -> SocketAddr {
244253

245254
if res < 0 && std::io::Error::last_os_error().raw_os_error() == Some(libc::EADDRNOTAVAIL) {
246255
log::info!("IPv6 is disabled on this host, falling back to IPv4 sockets");
247-
return SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port);
256+
return false;
248257
}
249258

250259
// Check if dual stack sockets are enabled by querying the socket option
@@ -263,11 +272,11 @@ pub fn select_bind_addr(port: u16) -> SocketAddr {
263272
log::info!(
264273
"IPv6 dual stack sockets are unavailable on this host, falling back to IPv4 sockets"
265274
);
266-
return SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port);
275+
return false;
267276
}
268277
}
269278

270-
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), port)
279+
true
271280
}
272281

273282
#[cfg(test)]

0 commit comments

Comments
 (0)