From 0f4ed1d64d93da5dfd968a4a0866cd7b94b2d04e Mon Sep 17 00:00:00 2001 From: Maxime Van Hees Date: Tue, 2 Sep 2025 15:17:52 +0200 Subject: [PATCH] working VM setup --- packages/system/virt/src/cloudhv/mod.rs | 18 +++++++++++++- packages/system/virt/src/cloudhv/net/mod.rs | 26 ++++++++++++++++----- packages/system/virt/src/image_prep/mod.rs | 20 ++++++++++------ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/system/virt/src/cloudhv/mod.rs b/packages/system/virt/src/cloudhv/mod.rs index 4596451..0bb2b8f 100644 --- a/packages/system/virt/src/cloudhv/mod.rs +++ b/packages/system/virt/src/cloudhv/mod.rs @@ -437,7 +437,23 @@ pub fn vm_start(id: &str) -> Result<(), CloudHvError> { // Ensure bridge, NAT, and DHCP net::ensure_bridge(&nat.bridge_name, &nat.bridge_addr_cidr, ipv6_bridge_cidr.as_deref())?; - net::ensure_nat(&nat.subnet_cidr)?; + // Derive IPv6 subnet for NAT + let ipv6_subnet = ipv6_bridge_cidr.as_ref().map(|cidr| { + let parts: Vec<&str> = cidr.split('/').collect(); + if parts.len() == 2 { + let addr = parts[0]; + if let Ok(ip) = addr.parse::() { + let seg = ip.segments(); + let pfx = std::net::Ipv6Addr::new(seg[0], seg[1], seg[2], seg[3], 0, 0, 0, 0); + format!("{}/64", pfx) + } else { + "".to_string() + } + } else { + "".to_string() + } + }); + net::ensure_nat(&nat.subnet_cidr, ipv6_subnet.as_deref())?; let lease_used = net::ensure_dnsmasq( &nat.bridge_name, &nat.dhcp_start, diff --git a/packages/system/virt/src/cloudhv/net/mod.rs b/packages/system/virt/src/cloudhv/net/mod.rs index a831027..9374230 100644 --- a/packages/system/virt/src/cloudhv/net/mod.rs +++ b/packages/system/virt/src/cloudhv/net/mod.rs @@ -86,7 +86,8 @@ sysctl -w net.ipv4.ip_forward=1 >/dev/null || true /// Ensure nftables NAT masquerading for the given subnet toward the default WAN interface. /// Creates table/chain if missing and adds/keeps a single masquerade rule. -pub fn ensure_nat(subnet_cidr: &str) -> Result<(), CloudHvError> { +/// If ipv6_subnet is provided, also sets up IPv6 NAT. +pub fn ensure_nat(subnet_cidr: &str, ipv6_subnet: Option<&str>) -> Result<(), CloudHvError> { for bin in ["ip", "nft"] { if sal_process::which(bin).is_none() { return Err(CloudHvError::DependencyMissing(format!( @@ -95,23 +96,34 @@ pub fn ensure_nat(subnet_cidr: &str) -> Result<(), CloudHvError> { ))); } } + let v6_subnet = ipv6_subnet.unwrap_or(""); let body = format!( "set -e SUBNET={subnet} +IPV6_SUBNET={v6subnet} WAN_IF=$(ip -o route show default | awk '{{print $5}}' | head -n1) if [ -z \"$WAN_IF\" ]; then - echo \"No default WAN interface detected (required for IPv4 NAT)\" >&2 + echo \"No default WAN interface detected (required for NAT)\" >&2 exit 2 fi +# IPv4 NAT nft list table ip hero >/dev/null 2>&1 || nft add table ip hero nft list chain ip hero postrouting >/dev/null 2>&1 || nft add chain ip hero postrouting {{ type nat hook postrouting priority 100 \\; }} -# Only add rule if not present nft list chain ip hero postrouting | grep -q \"ip saddr $SUBNET oifname \\\"$WAN_IF\\\" masquerade\" \ || nft add rule ip hero postrouting ip saddr $SUBNET oifname \"$WAN_IF\" masquerade + +# IPv6 NAT (if subnet provided) +if [ -n \"$IPV6_SUBNET\" ]; then + nft list table ip6 hero >/dev/null 2>&1 || nft add table ip6 hero + nft list chain ip6 hero postrouting >/dev/null 2>&1 || nft add chain ip6 hero postrouting {{ type nat hook postrouting priority 100 \\; }} + nft list chain ip6 hero postrouting | grep -q \"ip6 saddr $IPV6_SUBNET oifname \\\"$WAN_IF\\\" masquerade\" \ + || nft add rule ip6 hero postrouting ip6 saddr $IPV6_SUBNET oifname \"$WAN_IF\" masquerade +fi ", subnet = shell_escape(subnet_cidr), + v6subnet = shell_escape(v6_subnet), ); run_heredoc("HERONAT", &body) } @@ -174,11 +186,13 @@ printf '%s\\n' \ # Optional IPv6 RA/DHCPv6 if [ -n \"$IPV6_CIDR\" ]; then + BRIDGE_ADDR=\"${{IPV6_CIDR%/*}}\" + BRIDGE_PREFIX=$(echo \"$IPV6_CIDR\" | cut -d: -f1-4):: printf '%s\\n' \ \"enable-ra\" \ - \"dhcp-range=::,constructor:BR_PLACEHOLDER,ra-names,64,12h\" \ - \"dhcp-option=option6:dns-server,[2001:4860:4860::8888],[2606:4700:4700::1111]\" >>\"$TMP\" - sed -i \"s/BR_PLACEHOLDER/$BR/g\" \"$TMP\" + \"dhcp-range=$BRIDGE_PREFIX,ra-names,12h\" \ + \"dhcp-option=option6:dns-server,[2001:4860:4860::8888]\" \ + \"dhcp-option=option6:route,3600,400::/7,[$BRIDGE_ADDR]\" >>\"$TMP\" fi if [ ! -f \"$CFG\" ] || ! cmp -s \"$CFG\" \"$TMP\"; then diff --git a/packages/system/virt/src/image_prep/mod.rs b/packages/system/virt/src/image_prep/mod.rs index f399bca..4fb9b1b 100644 --- a/packages/system/virt/src/image_prep/mod.rs +++ b/packages/system/virt/src/image_prep/mod.rs @@ -175,11 +175,11 @@ pub fn image_prepare(opts: &ImagePrepOptions) -> Result Result Result