Skip to content

Commit f62aa67

Browse files
committed
feat: Enable ipv6, improve interface/nic filter
* Report ipv6 interfaces * Modify the `--interfaces` config option to allow more sophisticated filtering based on a nics name, address, protocol and nic type. Works the same as on the C++ server nodes and is compatible with existing config.
1 parent dac9f14 commit f62aa67

File tree

9 files changed

+508
-78
lines changed

9 files changed

+508
-78
lines changed

mgmtd/assets/beegfs-mgmtd.toml

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,35 @@
3737
# The private key file belonging to the above certificate.
3838
# tls-key-file = "/etc/beegfs/key.pem"
3939

40-
# Restricts network interfaces reported to other nodes for incoming BeeMsg communication.
41-
# The interfaces are reported in the given order. If not given, all suitable interfaces can be used.
42-
# interfaces = ["eth0", "eth1"]
40+
# Restricts and prioritizes network interfaces reported to other nodes for incoming BeeMsg
41+
# communication.
42+
#
43+
# Accepts a list of interface/nic filters. Interfaces can be filtered by name, address and protocol
44+
# (ipv4 or ipv6). Each filter entry has the form `[!] [<name>|*] [<addr>|*] [<protocol>|*]`, where
45+
# protocol can be "4" or "6". Each field can be set to "*" to match any value. Stars on the right
46+
# can be omitted. The order of the filter entries determines the priority of the interfaces as they
47+
# should be used by other nodes for BeeMsg communication. The first entry an interface matches is
48+
# that interfaces priority - the earlier the match, the higher the priority. Any interface that
49+
# doesn't match any entry is not reported and will thus not be contacted by other nodes. A single
50+
# `!` before the entry blacklists the matching interfaces - it is not reported even if a later entry
51+
# does match it.
52+
#
53+
# If not given, all suitable interfaces can be used and are reported in default order.
54+
#
55+
# EXAMPLES:
56+
#
57+
# * Prefer IPv6: ["* * 6", "* * 4"]
58+
# * IPv6 only: ["* * 6"]
59+
# * Only the eth0 interface using IPv6: ["eth0 * 6"]
60+
# * Prefer one IPv6 address, allow only IPv4 otherwise: ["* fd00::1", "* * 4"]
61+
# * Deny eth0 interface, allow everything else: ["! eth0", "*"]
62+
#
63+
# interfaces = ["*"]
64+
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
4369

4470
# Maximum number of outgoing connections per node.
4571
# connection-limit = 12

mgmtd/src/config.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use anyhow::{Context, Result, bail};
55
use clap::{Parser, ValueEnum};
66
use log::LevelFilter;
77
use serde::{Deserialize, Deserializer};
8+
use shared::nic::{self, NicFilter};
89
use shared::parser::{duration, integer_range};
910
use shared::types::{Port, QuotaId};
1011
use std::fmt::Debug;
@@ -203,14 +204,33 @@ generate_structs! {
203204
#[arg(value_name = "PATH")]
204205
tls_key_file: PathBuf = "/etc/beegfs/key.pem".into(),
205206

206-
/// Restricts network interfaces reported to other nodes for incoming BeeMsg communication.
207+
/// Restricts and prioritizes network interfaces reported to other nodes for incoming BeeMsg
208+
/// communication.
207209
///
208-
/// Accepts a comma separated list of interface names. They are reported in the given order. If
209-
/// not given, all suitable interfaces can be used.
210+
/// Accepts a comma separated list of interface/nic filters. Interfaces can be filtered by
211+
/// name, address and protocol (ipv4 or ipv6). Each filter entry has the form `[!] [<name>|*]
212+
/// [<addr>|*] [<protocol>|*]`, where protocol can be "4" or "6". Each field can be set to
213+
/// "*" to match any value. Stars on the right can be omitted. The order of the filter entries
214+
/// determines the priority of the interfaces as they should be used by other nodes for BeeMsg
215+
/// communication. The first entry an interface matches is that interfaces priority - the
216+
/// earlier the match, the higher the priority. Any interface that doesn't match any entry is
217+
/// not reported and will thus not be contacted by other nodes. A single `!` before the entry
218+
/// blacklists the matching interfaces - it is not reported even if a later entry does match it.
219+
///
220+
/// If not given, all suitable interfaces can be used and are reported in default order.
221+
///
222+
/// EXAMPLES:
223+
///
224+
/// * Prefer IPv6: `* * 6,* * 4`
225+
/// * IPv6 only: `* * 6`
226+
/// * Only the eth0 interface using IPv6: `eth0 * 6`
227+
/// * Prefer one IPv6 address, allow only IPv4 otherwise: `* fd00::1,* * 4`
228+
/// * Deny eth0 interface, allow everything else: `! eth0,*`
210229
#[arg(long)]
211-
#[arg(value_name = "NAMES")]
230+
#[arg(value_name = "FILTERS")]
212231
#[arg(value_delimiter = ',')]
213-
interfaces: Vec<String> = vec![],
232+
#[arg(value_parser = nic::NicFilter::parse)]
233+
interfaces: Vec<NicFilter> = vec![],
214234

215235
/// Maximum number of outgoing BeeMsg connections per node. [default: 12]
216236
#[arg(long)]

mgmtd/src/grpc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ pub(crate) fn serve(ctx: Context, mut shutdown: RunStateHandle) -> Result<()> {
181181
builder
182182
};
183183

184-
let serve_addr = SocketAddr::new("0.0.0.0".parse()?, ctx.info.user_config.grpc_port);
184+
let serve_addr = SocketAddr::new("::".parse()?, ctx.info.user_config.grpc_port);
185185

186186
let service = pm::management_server::ManagementServer::with_interceptor(
187187
ManagementService { ctx: ctx.clone() },

mgmtd/src/grpc/node.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use super::*;
22
use shared::bee_msg::node::RemoveNode;
3+
use std::net::{IpAddr, Ipv6Addr};
4+
use std::str::FromStr;
35

46
/// Delivers a list of nodes
57
pub(crate) async fn get(ctx: Context, req: pm::GetNodesRequest) -> Result<pm::GetNodesResponse> {
@@ -139,7 +141,11 @@ pub(crate) async fn get(ctx: Context, req: pm::GetNodesRequest) -> Result<pm::Ge
139141
.filter(|(uid, _)| node.id.as_ref().is_some_and(|e| e.uid == Some(*uid)))
140142
.cloned()
141143
.map(|(_, mut nic)| {
142-
nic.addr = format!("{}:{}", nic.addr, node.port);
144+
nic.addr = SocketAddr::new(
145+
IpAddr::from_str(&nic.addr).unwrap_or(Ipv6Addr::UNSPECIFIED.into()),
146+
node.port as u16,
147+
)
148+
.to_string();
143149
nic
144150
})
145151
.collect();

mgmtd/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ use anyhow::Result;
1818
use bee_msg::notify_nodes;
1919
use db::node_nic::ReplaceNic;
2020
use license::LicenseVerifier;
21-
use shared::NetworkAddr;
2221
use shared::bee_msg::target::RefreshTargetStates;
2322
use shared::conn::{Pool, incoming};
23+
use shared::nic::Nic;
2424
use shared::run_state::{self, RunStateControl};
2525
use shared::types::{AuthSecret, MGMTD_UID, NicType, NodeId, NodeType};
2626
use sqlite::TransactionExt;
@@ -39,7 +39,7 @@ use types::SqliteEnumExt;
3939
pub struct StaticInfo {
4040
pub user_config: Config,
4141
pub auth_secret: Option<AuthSecret>,
42-
pub network_addrs: Vec<NetworkAddr>,
42+
pub network_addrs: Vec<Nic>,
4343
}
4444

4545
/// Starts the management service.
@@ -63,7 +63,7 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result<RunCont
6363
// UDP socket for in- and outgoing messages
6464
let udp_socket = Arc::new(
6565
UdpSocket::bind(SocketAddr::new(
66-
"0.0.0.0".parse()?,
66+
"::0".parse()?,
6767
info.user_config.beemsg_port,
6868
))
6969
.await?,
@@ -94,7 +94,7 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result<RunCont
9494
MGMTD_UID,
9595
info.network_addrs.iter().map(|e| ReplaceNic {
9696
nic_type: NicType::Ethernet,
97-
addr: &e.addr,
97+
addr: &e.address,
9898
name: e.name.as_str().into(),
9999
}),
100100
)
@@ -122,7 +122,7 @@ pub async fn start(info: StaticInfo, license: LicenseVerifier) -> Result<RunCont
122122

123123
// Listen for incoming TCP connections
124124
incoming::listen_tcp(
125-
SocketAddr::new("0.0.0.0".parse()?, ctx.info.user_config.beemsg_port),
125+
SocketAddr::new("::0".parse()?, ctx.info.user_config.beemsg_port),
126126
ctx.clone(),
127127
info.auth_secret.is_some(),
128128
run_state.clone(),

mgmtd/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ doc.beegfs.io.",
9797
None
9898
};
9999

100-
let network_addrs = shared::ethernet_interfaces(&user_config.interfaces)?;
100+
let network_addrs = shared::nic::query_nics(&user_config.interfaces)?;
101101

102102
// Configure the tokio runtime
103103
let rt = tokio::runtime::Builder::new_multi_thread()

shared/src/lib.rs

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -13,66 +13,7 @@ pub mod conn;
1313
#[cfg(feature = "grpc")]
1414
pub mod grpc;
1515
pub mod journald_logger;
16+
pub mod nic;
1617
pub mod parser;
1718
pub mod run_state;
1819
pub mod types;
19-
20-
use anyhow::{Result, bail};
21-
use std::net::IpAddr;
22-
23-
#[derive(Debug, Clone)]
24-
pub struct NetworkAddr {
25-
pub addr: IpAddr,
26-
pub name: String,
27-
}
28-
29-
/// Retrieve the systems available network interfaces with their addresses
30-
///
31-
/// Only interfaces matching one of the given names in `filter` will be returned, unless the list
32-
/// is empty.
33-
pub fn ethernet_interfaces(filter: &[impl AsRef<str>]) -> Result<Vec<NetworkAddr>> {
34-
let mut filtered_nics = vec![];
35-
for interface in pnet_datalink::interfaces() {
36-
if !filter.is_empty() && !filter.iter().any(|e| interface.name == e.as_ref()) {
37-
continue;
38-
}
39-
40-
for ip in interface.ips {
41-
// TODO Ipv6: Remove the Ipv4 filter when protocol changes (https://github.com/ThinkParQ/beegfs-rs/issues/145)
42-
if !ip.is_ipv4() {
43-
continue;
44-
}
45-
46-
filtered_nics.push(NetworkAddr {
47-
addr: ip.ip(),
48-
name: interface.name.clone(),
49-
});
50-
}
51-
}
52-
53-
// Check all filters have been used
54-
if !filter
55-
.iter()
56-
.all(|e| filtered_nics.iter().any(|g| g.name == e.as_ref()))
57-
{
58-
bail!("At least one network interface doesn't exist");
59-
}
60-
61-
// Sort
62-
filtered_nics.sort_unstable_by_key(|k| {
63-
if filter.is_empty() {
64-
// Move loopbacks to the back
65-
k.addr.is_loopback() as usize
66-
} else {
67-
// Sort by filter
68-
filter
69-
.iter()
70-
.enumerate()
71-
.find(|e| e.1.as_ref() == k.name)
72-
.map(|e| e.0)
73-
.unwrap_or(usize::MAX)
74-
}
75-
});
76-
77-
Ok(filtered_nics)
78-
}

0 commit comments

Comments
 (0)