Skip to content

Commit b3dfac2

Browse files
committed
feat(dhcp): include the host's IPv6 DNS resolvers in the agent's DHCP config
The agent reads a host's nameservers from the DPU's dual-stack `ServiceAddresses`, then builds the `DhcpConfig` it sends to `forge-dhcp-server`. It now splits those nameservers by family at both build sites -- the gRPC control path (`update_dhcp_via_grpc`) and the on-disk config path (`write_dhcp_v4_server_config`) -- and fills `carbide_nameservers_v6` with the IPv6 set, threaded through `from_forge_dhcp_config` and `build_server_config`. The agent used to filter these nameservers to IPv4 and drop the IPv6 ones, with a TODO marking exactly this gap. The IPv4 path is unchanged -- it still drives the DHCPv4 option-6 nameservers -- and the IPv6 set stays inert until a delivery channel consumes it (RA RDNSS or DHCPv6, see #2644 / #1263). Tests extend the file-path builder test to assert an IPv6 nameserver round-trips through the written config, alongside the builder's own dual-stack case. This supports #2640. Signed-off-by: Chet Nichols III <chetn@nvidia.com>
1 parent 7e9c6c9 commit b3dfac2

3 files changed

Lines changed: 42 additions & 28 deletions

File tree

crates/agent/src/dhcp.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
use std::net::Ipv4Addr;
17+
use std::net::{Ipv4Addr, Ipv6Addr};
1818

1919
use ::rpc::forge as rpc;
2020
use carbide_rpc_utils::dhcp::HostConfig;
@@ -64,12 +64,14 @@ pub fn build_server_config(
6464
pxe_ip: Ipv4Addr,
6565
ntpservers: Vec<Ipv4Addr>,
6666
nameservers: Vec<Ipv4Addr>,
67+
nameservers_v6: Vec<Ipv6Addr>,
6768
loopback_ip: Ipv4Addr,
6869
) -> Result<String, eyre::Report> {
6970
let dhcp_config = carbide_rpc_utils::dhcp::DhcpConfig::from_forge_dhcp_config(
7071
pxe_ip,
7172
ntpservers,
7273
nameservers,
74+
nameservers_v6,
7375
loopback_ip,
7476
)?;
7577

crates/agent/src/ethernet_virtualization.rs

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::collections::HashMap;
1919
use std::ffi::CStr;
2020
use std::fs::File;
2121
use std::io::Read;
22-
use std::net::{IpAddr, Ipv4Addr};
22+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2323
use std::path::{Path, PathBuf};
2424
use std::str::FromStr;
2525
use std::time::Duration;
@@ -138,6 +138,20 @@ pub struct ServiceAddresses {
138138
pub nameservers: Vec<IpAddr>,
139139
}
140140

141+
/// Split a dual-stack nameserver list into its IPv4 and IPv6 members, so the
142+
/// gRPC and file-write DHCP-config paths derive both families the same way.
143+
fn split_nameservers_by_family(nameservers: &[IpAddr]) -> (Vec<Ipv4Addr>, Vec<Ipv6Addr>) {
144+
let mut v4 = Vec::new();
145+
let mut v6 = Vec::new();
146+
for addr in nameservers {
147+
match addr {
148+
IpAddr::V4(v4_addr) => v4.push(*v4_addr),
149+
IpAddr::V6(v6_addr) => v6.push(*v6_addr),
150+
}
151+
}
152+
(v4, v6)
153+
}
154+
141155
fn build_dhcp_ntp_servers(
142156
nc: &rpc::ManagedHostNetworkConfigResponse,
143157
service_addrs: &ServiceAddresses,
@@ -1066,14 +1080,7 @@ async fn update_dhcp_via_grpc(
10661080
};
10671081
let loopback_ip: Ipv4Addr = mh_nc.loopback_ip.parse()?;
10681082

1069-
let nameservers_v4 = service_addrs
1070-
.nameservers
1071-
.iter()
1072-
.filter_map(|x| match x {
1073-
IpAddr::V4(x) => Some(*x),
1074-
_ => None,
1075-
})
1076-
.collect::<Vec<Ipv4Addr>>();
1083+
let (nameservers_v4, nameservers_v6) = split_nameservers_by_family(&service_addrs.nameservers);
10771084

10781085
let ntpservers_v4 = build_dhcp_ntp_servers(network_config, service_addrs);
10791086

@@ -1095,6 +1102,7 @@ async fn update_dhcp_via_grpc(
10951102
pxe_ip_v4,
10961103
ntpservers_v4,
10971104
nameservers_v4,
1105+
nameservers_v6,
10981106
loopback_ip,
10991107
)?;
11001108
let mut host_config = carbide_rpc_utils::dhcp::HostConfig::try_from(
@@ -1471,18 +1479,9 @@ fn write_dhcp_v4_server_config(
14711479

14721480
let loopback_ip = mh_nc.loopback_ip.parse()?;
14731481

1474-
// Filter to IPv4, since this is specifically for the DHCPv4 server
1475-
// config, and the input ServiceAddresses holds both families.
1476-
// Again, we'll eventually have a specific builder for a DHCPv6
1477-
// that does similar things with ServiceAddresses, but for IPv6.
1478-
let nameservers_v4 = service_addrs
1479-
.nameservers
1480-
.iter()
1481-
.filter_map(|x| match x {
1482-
IpAddr::V4(x) => Some(*x),
1483-
_ => None,
1484-
})
1485-
.collect::<Vec<Ipv4Addr>>();
1482+
// The IPv4 set drives the DHCPv4 options written here; the IPv6 set is held
1483+
// in the config for the eventual DHCPv6 / RA consumer (inert in this path).
1484+
let (nameservers_v4, nameservers_v6) = split_nameservers_by_family(&service_addrs.nameservers);
14861485

14871486
let ntpservers_v4 = build_dhcp_ntp_servers(nc, service_addrs);
14881487

@@ -1517,8 +1516,13 @@ fn write_dhcp_v4_server_config(
15171516
Err(err) => tracing::error!("Write DHCP server {}: {err:#}", dhcp_server_path.server),
15181517
}
15191518

1520-
let next_contents =
1521-
dhcp::build_server_config(pxe_ip_v4, ntpservers_v4, nameservers_v4, loopback_ip)?;
1519+
let next_contents = dhcp::build_server_config(
1520+
pxe_ip_v4,
1521+
ntpservers_v4,
1522+
nameservers_v4,
1523+
nameservers_v6,
1524+
loopback_ip,
1525+
)?;
15221526
match write(
15231527
next_contents,
15241528
&dhcp_server_path.config,
@@ -3467,7 +3471,7 @@ mod tests {
34673471
rebinding_time_secs: 432000,
34683472
carbide_api_url: None,
34693473
carbide_dhcp_server: Ipv4Addr::from([10, 217, 5, 39]),
3470-
carbide_nameservers_v6: vec![],
3474+
carbide_nameservers_v6: vec!["2001:db8::53".parse().unwrap()],
34713475
};
34723476

34733477
let mut network_config = rpc::ManagedHostNetworkConfigResponse {
@@ -3565,7 +3569,7 @@ mod tests {
35653569
IpAddr::from([127, 0, 0, 2]),
35663570
IpAddr::from([127, 0, 0, 3]),
35673571
],
3568-
nameservers: vec![IpAddr::from([10, 1, 1, 1])],
3572+
nameservers: vec![IpAddr::from([10, 1, 1, 1]), "2001:db8::53".parse().unwrap()],
35693573
};
35703574

35713575
let mut host_config_str =

crates/rpc-utils/src/dhcp.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,12 @@ impl DhcpConfig {
8888
carbide_provisioning_server_ipv4: Ipv4Addr,
8989
carbide_ntpservers: Vec<Ipv4Addr>,
9090
carbide_nameservers: Vec<Ipv4Addr>,
91+
carbide_nameservers_v6: Vec<Ipv6Addr>,
9192
loopback_ip: Ipv4Addr,
9293
) -> Result<Self, DhcpDataError> {
9394
Ok(DhcpConfig {
9495
carbide_nameservers,
96+
carbide_nameservers_v6,
9597
carbide_ntpservers,
9698
carbide_provisioning_server_ipv4,
9799
carbide_dhcp_server: loopback_ip,
@@ -303,7 +305,7 @@ impl IntoIterator for DhcpTimestamps {
303305

304306
#[cfg(test)]
305307
mod tests {
306-
use std::net::Ipv4Addr;
308+
use std::net::{Ipv4Addr, Ipv6Addr};
307309

308310
use carbide_test_support::Outcome::*;
309311
use carbide_test_support::{scenarios, value_scenarios};
@@ -323,6 +325,7 @@ mod tests {
323325
dhcp_server: Ipv4Addr,
324326
ntpservers: Vec<Ipv4Addr>,
325327
nameservers: Vec<Ipv4Addr>,
328+
nameservers_v6: Vec<Ipv6Addr>,
326329
lease_time_secs: u32,
327330
}
328331

@@ -413,24 +416,27 @@ mod tests {
413416
}
414417

415418
fn summarize_dhcp_config(
416-
(provisioning_server, ntpservers, nameservers, dhcp_server): (
419+
(provisioning_server, ntpservers, nameservers, nameservers_v6, dhcp_server): (
417420
Ipv4Addr,
418421
Vec<Ipv4Addr>,
419422
Vec<Ipv4Addr>,
423+
Vec<Ipv6Addr>,
420424
Ipv4Addr,
421425
),
422426
) -> Result<DhcpConfigSummary, &'static str> {
423427
DhcpConfig::from_forge_dhcp_config(
424428
provisioning_server,
425429
ntpservers,
426430
nameservers,
431+
nameservers_v6,
427432
dhcp_server,
428433
)
429434
.map(|config| DhcpConfigSummary {
430435
provisioning_server: config.carbide_provisioning_server_ipv4,
431436
dhcp_server: config.carbide_dhcp_server,
432437
ntpservers: config.carbide_ntpservers,
433438
nameservers: config.carbide_nameservers,
439+
nameservers_v6: config.carbide_nameservers_v6,
434440
lease_time_secs: config.lease_time_secs,
435441
})
436442
.map_err(dhcp_error_kind)
@@ -463,12 +469,14 @@ mod tests {
463469
Ipv4Addr::new(192, 0, 2, 10),
464470
vec![Ipv4Addr::new(192, 0, 2, 20)],
465471
vec![Ipv4Addr::new(192, 0, 2, 53)],
472+
vec!["2001:db8::53".parse::<Ipv6Addr>().unwrap()],
466473
Ipv4Addr::new(127, 0, 0, 2),
467474
) => Yields(DhcpConfigSummary {
468475
provisioning_server: Ipv4Addr::new(192, 0, 2, 10),
469476
dhcp_server: Ipv4Addr::new(127, 0, 0, 2),
470477
ntpservers: vec![Ipv4Addr::new(192, 0, 2, 20)],
471478
nameservers: vec![Ipv4Addr::new(192, 0, 2, 53)],
479+
nameservers_v6: vec!["2001:db8::53".parse::<Ipv6Addr>().unwrap()],
472480
lease_time_secs: DEFAULT_LEASE_TIME_SECS,
473481
}),
474482
}

0 commit comments

Comments
 (0)