From 657b66815dc4eda200f54cecd4229cceb3a6dd35 Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Thu, 19 Dec 2024 11:04:14 -0500 Subject: [PATCH] [skip ci] correct addresspool implementation --- .../router_server/default_config.yml | 14 +- .../machine_registry/address_pool.rs | 365 +++++++++++++++--- .../machine_registry_inner.rs | 11 +- .../router_server/machine_registry/mod.rs | 1 + .../machine_registry/state/blueprint_state.rs | 68 +++- .../machine_registry/state/machine_state.rs | 83 +++- .../machine_registry/state/network_state.rs | 103 ++++- .../machine_registry/state/template_state.rs | 3 +- 8 files changed, 554 insertions(+), 94 deletions(-) diff --git a/veilid-tools/src/virtual_network/router_server/default_config.yml b/veilid-tools/src/virtual_network/router_server/default_config.yml index d6cd6fde..f85020ca 100644 --- a/veilid-tools/src/virtual_network/router_server/default_config.yml +++ b/veilid-tools/src/virtual_network/router_server/default_config.yml @@ -128,22 +128,22 @@ blueprints: # with both ipv4 and ipv6 networking direct: ipv4: - additional_prefix: 24 + prefix: 24 ipv6: - additional_prefix: 64 + prefix: 64 # An ipv4-only subnet of the internet directly attached with no translation direct_ipv4_no_ipv6: ipv4: - additional_prefix: 24 + prefix: 24 # An ipv6-only subnet of the internet directly attached with no translation direct_ipv6_no_ipv4: ipv6: - additional_prefix: 64 + prefix: 64 # An ipv4-only subnet of the internet attached via NAT nat_ipv4_no_ipv6: ipv4: allocation: "$private" - additional_prefix: 0 + prefix: 24 gateway: translation: "port_restricted" upnp: 0.25 @@ -152,12 +152,12 @@ blueprints: nat_ipv4_direct_ipv6: ipv4: allocation: "$private" - additional_prefix: 0 + prefix: 24 gateway: translation: "port_restricted" upnp: 0.25 ipv6: - additional_prefix: 56 + prefix: 56 ################################################################# # Allocations diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/address_pool.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/address_pool.rs index f4ed8b0e..6791b795 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/address_pool.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/address_pool.rs @@ -4,6 +4,10 @@ use ipnet::*; #[derive(Debug)] pub struct AddressPool { srng: StableRng, + + scope_v4: Vec, + scope_v6: Vec, + allocated_v4: Vec, allocated_v6: Vec, } @@ -12,90 +16,349 @@ impl AddressPool { pub fn new(srng: StableRng) -> Self { Self { srng, + scope_v4: Vec::new(), + scope_v6: Vec::new(), allocated_v4: Vec::new(), allocated_v6: Vec::new(), } } - pub fn contains_v4(&self, allocation: &Ipv4Net) -> bool { - for x in &self.allocated_v4 { - if x.contains(allocation) { + ///////////////////////////////////////////////////////////////////// + + pub fn add_scope_v4(&mut self, allocation: Ipv4Net) { + self.scope_v4.push(allocation); + self.scope_v4 = Ipv4Net::aggregate(&self.scope_v4); + } + + pub fn add_scope_v6(&mut self, allocation: Ipv6Net) { + self.scope_v6.push(allocation); + self.scope_v6 = Ipv6Net::aggregate(&self.scope_v6); + } + + pub fn is_in_scope_v4(&self, allocation: Ipv4Net) -> bool { + for x in &self.scope_v4 { + if x.contains(&allocation) { return true; } } false } - pub fn contains_v6(&self, allocation: &Ipv6Net) -> bool { - for x in &self.allocated_v6 { - if x.contains(allocation) { + pub fn is_in_scope_v6(&self, allocation: Ipv6Net) -> bool { + for x in &self.scope_v6 { + if x.contains(&allocation) { return true; } } false } - pub fn add_v4(&mut self, allocation: Ipv4Net) { + ///////////////////////////////////////////////////////////////////// + + pub fn allocate_v4(&mut self, allocation: Ipv4Net) -> MachineRegistryResult<()> { + // Ensure the allocation is in our scope + if !self.is_in_scope_v4(allocation) { + return Err(MachineRegistryError::NoAllocation); + } + + // Add to our allocated pool self.allocated_v4.push(allocation); self.allocated_v4 = Ipv4Net::aggregate(&self.allocated_v4); + + Ok(()) } - pub fn add_v6(&mut self, allocation: Ipv6Net) { + pub fn allocate_v6(&mut self, allocation: Ipv6Net) -> MachineRegistryResult<()> { + // Ensure the allocation is in our scope + if !self.is_in_scope_v6(allocation) { + return Err(MachineRegistryError::NoAllocation); + } + + // Add to our allocated pool self.allocated_v6.push(allocation); self.allocated_v6 = Ipv6Net::aggregate(&self.allocated_v6); + + Ok(()) } - pub fn add_random_subnet_v4( - &mut self, - allocation: &Ipv4Net, - additional_prefix: u8, - ) -> Option { - // Apply the additional prefix - let prefix = u8::max( - allocation.prefix_len() + additional_prefix, - allocation.max_prefix_len(), - ); - - // Get the subnets with this prefix - let mut subnets = allocation.subnets(prefix).ok()?.collect::>(); - - // Randomize the subnets - self.srng.shuffle_vec(&mut subnets); - - // Pick the first available subnet - for subnet in subnets { - if !self.contains_v4(&subnet) { - self.add_v4(subnet); - return Some(subnet); + pub fn get_overlaps_v4(&self, allocation: Ipv4Net) -> Vec { + let mut overlaps = Vec::::new(); + for x in &self.allocated_v4 { + if x.contains(&allocation) || allocation.contains(x) { + overlaps.push(*x); + overlaps = Ipv4Net::aggregate(&overlaps); } } + overlaps + } + + pub fn get_overlaps_v6(&self, allocation: Ipv6Net) -> Vec { + let mut overlaps = Vec::::new(); + for x in &self.allocated_v6 { + if x.contains(&allocation) || allocation.contains(x) { + overlaps.push(*x); + overlaps = Ipv6Net::aggregate(&overlaps); + } + } + overlaps + } + + fn range_in_prefix_32(scope_prefix: u8, iterable_prefix_bits: u8) -> u32 { + // If we're allocating addresses, exclude scope's network and broadcast address + if scope_prefix + iterable_prefix_bits == 32 { + // Subtract two from total + if scope_prefix == 0 { + // Overflow case + 0xFFFF_FFFEu32 + } else { + // Non-overflow case + (1u32 << iterable_prefix_bits) - 2 + } + } else { + // network only iteration + 1u32 << iterable_prefix_bits + } + } + + fn range_in_prefix_128(scope_prefix: u8, iterable_prefix_bits: u8) -> u128 { + // If we're allocating addresses, exclude scope's network and broadcast address + if scope_prefix + iterable_prefix_bits == 128 { + // Subtract two from total + if scope_prefix == 0 { + // Overflow case + 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFEu128 + } else { + // Non-overflow case + (1u128 << iterable_prefix_bits) - 2 + } + } else { + // network only iteration + 1u128 << iterable_prefix_bits + } + } + + pub fn allocate_random_v4(&mut self, prefix: u8) -> Option { + // Scope ranges to iterate + let mut scope_ranges = Vec::<(Ipv4Net, u8, u32)>::new(); + let mut total_subnets = 0u32; + + // Build range set from scopes, minus the prefix to allocate + for scope in self.scope_v4.iter().copied() { + // If the prefix we are looking to allocate doesn't fit in this scope + // then we exclude it + if scope.prefix_len() > prefix { + continue; + } + + // Get the number of prefix bits we can iterate + let iterable_prefix_bits = prefix - scope.prefix_len(); + let iterable_range = Self::range_in_prefix_32(scope.prefix_len(), iterable_prefix_bits); + + // Scope ranges to try + scope_ranges.push((scope, iterable_prefix_bits, iterable_range)); + total_subnets += iterable_range; + } + if total_subnets == 0 { + // No range + return None; + } + + // Choose a random subnet to start with + let chosen_subnet_index = self.srng.next_u32(0, total_subnets - 1); + + // Find the starting scope and starting subnet index within + // the scope of the chosen subnet index + let mut scope_index = 0usize; + let mut scope_start_subnet_index = 0u32; + loop { + assert!( + scope_index < scope_ranges.len(), + "should always have chosen a starting point inside a scope" + ); + + let scope_end_subnet_index = scope_start_subnet_index + scope_ranges[scope_index].2; + if chosen_subnet_index < scope_end_subnet_index { + break; + } + + // chosen starting point is in the next scope + scope_index += 1; + scope_start_subnet_index = scope_end_subnet_index; + } + let initial_subnet_index = chosen_subnet_index; + let initial_scope_index = scope_index; + + // Iterate forward until we find a free range + let mut current_subnet_index = initial_subnet_index; + let mut current_scope_index = initial_scope_index; + let mut current_scope_start_subnet_index = scope_start_subnet_index; + let mut current_scope_end_subnet_index = + scope_start_subnet_index + scope_ranges[scope_index].2; + + let opt_allocation = loop { + // Get the net at this current subnet index + let netbits = u32::from(scope_ranges[current_scope_index].0.network()); + let subnetbits = if prefix == 32 { + // Allocating addresses + ((current_subnet_index - current_scope_start_subnet_index) + 1) << (32 - prefix) + } else { + // Allocating subnets + (current_subnet_index - current_scope_start_subnet_index) << (32 - prefix) + }; + let net = Ipv4Net::new(Ipv4Addr::from(netbits | subnetbits), prefix) + .expect("prefix must be valid"); + // See if this net is available + if self.get_overlaps_v4(net).is_empty() { + break Some(net); + } + // If not, go to the next subnet + current_subnet_index += 1; + + // If we got back to the beginning we failed to allocate + if current_scope_index == initial_scope_index + && current_subnet_index == initial_subnet_index + { + break None; + } + + // If we've reached the end of this scope then go to the next scope + if current_subnet_index == current_scope_end_subnet_index { + current_scope_index += 1; + // Wrap around + if current_scope_index == scope_ranges.len() { + current_subnet_index = 0; + current_scope_index = 0; + current_scope_start_subnet_index = 0; + } else { + current_scope_start_subnet_index = current_scope_end_subnet_index; + } + current_scope_end_subnet_index = + current_scope_start_subnet_index + scope_ranges[current_scope_index].2; + } + }; + + // If we found a free subnet, add it to our allocations + if let Some(allocation) = opt_allocation { + // Add to our allocated pool + self.allocated_v4.push(allocation); + self.allocated_v4 = Ipv4Net::aggregate(&self.allocated_v4); + return Some(allocation); + } + + // No allocation None } - pub fn add_random_subnet_v6( - &mut self, - allocation: &Ipv6Net, - additional_prefix: u8, - ) -> Option { - // Apply the additional prefix - let prefix = u8::max( - allocation.prefix_len() + additional_prefix, - allocation.max_prefix_len(), - ); + pub fn allocate_random_v6(&mut self, prefix: u8) -> Option { + // Scope ranges to iterate + let mut scope_ranges = Vec::<(Ipv6Net, u8, u128)>::new(); + let mut total_subnets = 0u128; - // Get the subnets with this prefix - let mut subnets = allocation.subnets(prefix).ok()?.collect::>(); - - // Randomize the subnets - self.srng.shuffle_vec(&mut subnets); - - // Pick the first available subnet - for subnet in subnets { - if !self.contains_v6(&subnet) { - self.add_v6(subnet); - return Some(subnet); + // Build range set from scopes, minus the prefix to allocate + for scope in self.scope_v6.iter().copied() { + // If the prefix we are looking to allocate doesn't fit in this scope + // then we exclude it + if scope.prefix_len() > prefix { + continue; } + + // Get the number of prefix bits we can iterate + let iterable_prefix_bits = prefix - scope.prefix_len(); + let iterable_range = + Self::range_in_prefix_128(scope.prefix_len(), iterable_prefix_bits); + + // Scope ranges to try + scope_ranges.push((scope, iterable_prefix_bits, iterable_range)); + total_subnets += iterable_range; } + if total_subnets == 0 { + // No range + return None; + } + + // Choose a random subnet to start with + let chosen_subnet_index = self.srng.next_u128(0, total_subnets - 1); + + // Find the starting scope and starting subnet index within + // the scope of the chosen subnet index + let mut scope_index = 0usize; + let mut scope_start_subnet_index = 0u128; + loop { + assert!( + scope_index < scope_ranges.len(), + "should always have chosen a starting point inside a scope" + ); + + let scope_end_subnet_index = scope_start_subnet_index + scope_ranges[scope_index].2; + if chosen_subnet_index < scope_end_subnet_index { + break; + } + + // chosen starting point is in the next scope + scope_index += 1; + scope_start_subnet_index = scope_end_subnet_index; + } + let initial_subnet_index = chosen_subnet_index; + let initial_scope_index = scope_index; + + // Iterate forward until we find a free range + let mut current_subnet_index = initial_subnet_index; + let mut current_scope_index = initial_scope_index; + let mut current_scope_start_subnet_index = scope_start_subnet_index; + let mut current_scope_end_subnet_index = + scope_start_subnet_index + scope_ranges[scope_index].2; + + let opt_allocation = loop { + // Get the net at this current subnet index + let netbits = u128::from(scope_ranges[current_scope_index].0.network()); + let subnetbits = if prefix == 128 { + // Allocating addresses + ((current_subnet_index - current_scope_start_subnet_index) + 1) << (128 - prefix) + } else { + // Allocating subnets + (current_subnet_index - current_scope_start_subnet_index) << (128 - prefix) + }; + let net = Ipv6Net::new(Ipv6Addr::from(netbits | subnetbits), prefix) + .expect("prefix must be valid"); + // See if this net is available + if self.get_overlaps_v6(net).is_empty() { + break Some(net); + } + // If not, go to the next subnet + current_subnet_index += 1; + + // If we got back to the beginning we failed to allocate + if current_scope_index == initial_scope_index + && current_subnet_index == initial_subnet_index + { + break None; + } + + // If we've reached the end of this scope then go to the next scope + if current_subnet_index == current_scope_end_subnet_index { + current_scope_index += 1; + // Wrap around + if current_scope_index == scope_ranges.len() { + current_subnet_index = 0; + current_scope_index = 0; + current_scope_start_subnet_index = 0; + } else { + current_scope_start_subnet_index = current_scope_end_subnet_index; + } + current_scope_end_subnet_index = + current_scope_start_subnet_index + scope_ranges[current_scope_index].2; + } + }; + + // If we found a free subnet, add it to our allocations + if let Some(allocation) = opt_allocation { + // Add to our allocated pool + self.allocated_v6.push(allocation); + self.allocated_v6 = Ipv6Net::aggregate(&self.allocated_v6); + return Some(allocation); + } + + // No allocation None } } diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs index c5aa72a0..b0633acf 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/machine_registry_inner.rs @@ -32,6 +32,9 @@ impl MachineRegistryInner { pub fn srng(&self) -> StableRng { self.unlocked_inner.srng.clone() } + pub fn config(&self) -> &config::Config { + &self.unlocked_inner.config + } pub fn execute_config(&self, cfg: config::Config) -> MachineRegistryResult<()> { // Create all networks @@ -479,7 +482,7 @@ impl MachineRegistryInner { // pub(super) fn choose_allocation_v4( // &mut self, // allocation: config::Allocation, - // additional_prefix: u8, + // prefix: u8, // ) -> MachineRegistryResult { // // Get allocation subnet candidates // let mut subnet4 = allocation @@ -495,7 +498,7 @@ impl MachineRegistryInner { // // Allocate within the subnet // match self // .address_pool - // .add_random_subnet_v4(subnet, additional_prefix) + // .add_random_subnet_v4(subnet, prefix) // { // Some(a) => { // // Got a sub-allocation @@ -520,7 +523,7 @@ impl MachineRegistryInner { // pub(super) fn choose_allocation_v6( // &mut self, // allocation: config::Allocation, - // additional_prefix: u8, + // prefix: u8, // ) -> MachineRegistryResult { // // Get allocation subnet candidates // let mut subnet6 = allocation @@ -536,7 +539,7 @@ impl MachineRegistryInner { // // Allocate within the subnet // match self // .address_pool - // .add_random_subnet_v6(subnet, additional_prefix) + // .add_random_subnet_v6(subnet, prefix) // { // Some(a) => { // // Got a sub-allocation diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs index 3cb3e014..aa0bc99d 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/mod.rs @@ -33,6 +33,7 @@ pub enum MachineRegistryError { NetworkNotFound, TemplateNotFound, BlueprintNotFound, + ModelNotFound, NoAllocation, } diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs index ae26edc9..5acd8cb5 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/blueprint_state.rs @@ -1,4 +1,5 @@ use super::*; +use ipnet::*; #[derive(Debug)] struct BlueprintStateUnlockedInner { @@ -9,14 +10,14 @@ struct BlueprintStateUnlockedInner { #[derive(Debug)] pub struct BlueprintStateIpv4Params { pub allocation: Option, - pub additional_prefix: u8, + pub prefix: u8, pub gateway: Option, } #[derive(Debug)] pub struct BlueprintStateIpv6Params { pub allocation: Option, - pub additional_prefix: u8, + pub prefix: u8, pub gateway: Option, } @@ -170,7 +171,24 @@ impl BlueprintState { machine_registry_inner: &mut MachineRegistryInner, network_state: NetworkState, ) -> MachineRegistryResult<()> { - xxx do generation + let model_name = match inner.model.clone() { + Some(models) => machine_registry_inner + .srng() + .weighted_choice(&models) + .clone(), + None => machine_registry_inner.config().default_model.clone(), + }; + let Some(model) = machine_registry_inner.config().models.get(&model_name) else { + return Err(MachineRegistryError::ModelNotFound); + }; + + let params = NetworkStateModelParams { + latency: Some(model.latency.clone()), + distance: model.distance.clone(), + loss: model.loss, + }; + network_state.set_model(params); + Ok(()) } fn generate_ipv4_inner( @@ -178,7 +196,49 @@ impl BlueprintState { machine_registry_inner: &mut MachineRegistryInner, network_state: NetworkState, ) -> MachineRegistryResult<()> { - // + network_state.clear_ipv4(machine_registry_inner); + let Some(ipv4) = inner.ipv4.as_ref() else { + return Ok(()); + }; + + let allocation = ipv4 + .params + .allocation + .clone() + .map(|x| { + let allocation = machine_registry_inner + .config() + .allocations + .get(&x) + .ok_or(MachineRegistryError::InvalidName)?; + if let Some(subnet4) = &allocation.subnets.subnet4 { + let ipv4net = machine_registry_inner + .srng() + .weighted_choice(subnet4) + .clone(); + Ok(Some(ipv4net)) + } else { + Ok(None) + } + }) + .unwrap_or_else(|| { + Ok(Some( + Ipv4Net::new(Ipv4Addr::UNSPECIFIED, 0).expect("must be valid"), + )) + })?; + let Some(allocation) = allocation else { + return Ok(()); + }; + + // Do suballocation + xxx figure out how to do allocation inside internet vs private network + + let params = NetworkStateIpv4Params { allocation }; + + let gateway_params = inner. + + network_state.set_ipv4(, gateway_params); + Ok(()) } fn generate_ipv6_inner( diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs index 48b6159c..92a125f7 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/machine_state.rs @@ -15,7 +15,7 @@ struct MachineStateInner { #[derive(Debug)] pub struct MachineStateInterface { /// The network this interface belongs to - pub network_id: NetworkStateId, + pub network_id: Option, /// The veilid NetworkInterface state pub network_interface: NetworkInterface, } @@ -94,7 +94,6 @@ impl MachineState { pub fn allocate_interface( &self, - network_id: NetworkStateId, opt_name: Option, opt_interface_flags: Option, ) -> MachineRegistryResult { @@ -112,7 +111,7 @@ impl MachineState { inner.interfaces.insert( name.clone(), MachineStateInterface { - network_id, + network_id: None, network_interface: NetworkInterface { name: name.clone(), flags, @@ -143,9 +142,12 @@ impl MachineState { }; // Get the network state + let Some(network_id) = intf.network_id else { + return Err(MachineRegistryError::NetworkNotFound); + }; let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)?; + .get_state(network_id)?; // Allocate interface address let is_dynamic = opt_address.is_none(); @@ -180,9 +182,12 @@ impl MachineState { }; // Get the network state + let Some(network_id) = intf.network_id else { + return Err(MachineRegistryError::NetworkNotFound); + }; let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)?; + .get_state(network_id)?; // Allocate interface address let is_dynamic = opt_address.is_none(); @@ -204,6 +209,47 @@ impl MachineState { Ok(ifv6_addr) } + pub fn attach_network( + &self, + machine_registry_inner: &mut MachineRegistryInner, + interface: &str, + network_id: NetworkStateId, + ) -> MachineRegistryResult<()> { + let mut inner = self.inner.lock(); + let Some(intf) = inner.interfaces.get_mut(interface) else { + return Err(MachineRegistryError::InvalidName); + }; + if intf.network_id.is_some() { + self.detach_network(machine_registry_inner, interface); + } + intf.network_id = Some(network_id); + + Ok(()) + } + + pub fn detach_network( + &self, + machine_registry_inner: &mut MachineRegistryInner, + interface: &str, + ) -> MachineRegistryResult<()> { + let mut inner = self.inner.lock(); + Self::detach_network_inner(&mut *inner, machine_registry_inner, interface) + } + + pub fn attached_network_interfaces( + &self, + network_id: NetworkStateId, + ) -> MachineRegistryResult> { + let mut out = Vec::new(); + let inner = self.inner.lock(); + for intf in &inner.interfaces { + if intf.1.network_id == Some(network_id) { + out.push(intf.0.clone()); + } + } + Ok(out) + } + pub fn release_address( &self, machine_registry_inner: &mut MachineRegistryInner, @@ -215,10 +261,14 @@ impl MachineState { return Err(MachineRegistryError::InvalidName); }; + let Some(network_id) = intf.network_id else { + return Err(MachineRegistryError::NetworkNotFound); + }; + // Get the network state let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)?; + .get_state(network_id)?; // Release the address from the network match address { @@ -243,6 +293,19 @@ impl MachineState { Self::release_all_addresses_inner(&mut *inner, machine_registry_inner, interface) } + fn detach_network_inner( + inner: &mut MachineStateInner, + machine_registry_inner: &mut MachineRegistryInner, + interface: &str, + ) -> MachineRegistryResult<()> { + Self::release_all_addresses_inner(inner, machine_registry_inner, interface)?; + let Some(intf) = inner.interfaces.get_mut(interface) else { + return Err(MachineRegistryError::InvalidName); + }; + intf.network_id = None; + Ok(()) + } + fn release_all_addresses_inner( inner: &mut MachineStateInner, machine_registry_inner: &mut MachineRegistryInner, @@ -252,10 +315,14 @@ impl MachineState { return Err(MachineRegistryError::InvalidName); }; + let Some(network_id) = intf.network_id else { + return Ok(()); + }; + // Get the network state let network_state = machine_registry_inner .network_states() - .get_state(intf.network_id)?; + .get_state(network_id)?; // Release the addresses from the network for addr in &intf.network_interface.addrs { @@ -277,7 +344,7 @@ impl MachineState { interface: &str, ) -> MachineRegistryResult<()> { let mut inner = self.inner.lock(); - Self::release_all_addresses_inner(&mut *inner, machine_registry_inner, interface)?; + Self::detach_network_inner(&mut *inner, machine_registry_inner, interface)?; inner .interfaces .remove(interface) diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs index f5c9966e..2550185f 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/network_state.rs @@ -23,17 +23,22 @@ struct NetworkStateInner { #[derive(Debug)] struct NetworkStateModel { - /// Network latency distribution - latency: Option, - /// Distance simulation metric - distance: Option, - /// Packet loss probability - loss: Probability, + params: NetworkStateModelParams, } #[derive(Debug)] -struct NetworkStateIpv4Params { - allocation: Ipv4Net, +pub struct NetworkStateModelParams { + /// Network latency distribution + pub latency: Option, + /// Distance simulation metric + pub distance: Option, + /// Packet loss probability + pub loss: Probability, +} + +#[derive(Debug)] +pub struct NetworkStateIpv4Params { + pub allocation: Ipv4Net, } #[derive(Debug)] @@ -44,8 +49,8 @@ struct NetworkStateIpv4 { } #[derive(Debug)] -struct NetworkStateIpv6Params { - allocation: Ipv6Net, +pub struct NetworkStateIpv6Params { + pub allocation: Ipv6Net, } #[derive(Debug)] struct NetworkStateIpv6 { @@ -55,10 +60,10 @@ struct NetworkStateIpv6 { } #[derive(Debug)] -struct NetworkStateGatewayParams { - translation: config::Translation, - upnp: bool, - network: Option, +pub struct NetworkStateGatewayParams { + pub translation: config::Translation, + pub upnp: bool, + pub network: Option, } #[derive(Debug)] @@ -88,9 +93,11 @@ impl NetworkState { inner: Arc::new(Mutex::new(NetworkStateInner { generating_blueprint: None, model: NetworkStateModel { - latency: None, - distance: None, - loss: 0.0, + params: NetworkStateModelParams { + latency: None, + distance: None, + loss: 0.0, + }, }, ipv4: None, ipv6: None, @@ -109,9 +116,38 @@ impl NetworkState { } } - pub fn set_model(&self, model: NetworkStateModel) { + pub fn set_model(&self, params: NetworkStateModelParams) { let mut inner = self.inner.lock(); - inner.model = model; + inner.model = NetworkStateModel { params }; + } + + pub fn clear_ipv4(&self, machine_registry_inner: &mut MachineRegistryInner) { + let mut inner = self.inner.lock(); + let Some(ipv4) = inner.ipv4.as_mut() else { + return; + }; + + let mut address_machines = HashMap::>::new(); + for (k, v) in &ipv4.machine_addresses { + address_machines.entry(*v).or_default().push(*k); + } + for (machine_id, addresses) in address_machines { + let machine_state = machine_registry_inner + .machine_states() + .get_state(machine_id) + .expect("must exist"); + + let interfaces = machine_state + .attached_network_interfaces(self.id()) + .expect("must exist"); + for address in addresses { + for interface in &interfaces { + machine_state + .release_address(machine_registry_inner, interface, IpAddr::V4(address)) + .expect("must succeed"); + } + } + } } pub fn set_ipv4( @@ -145,6 +181,35 @@ impl NetworkState { }) } } + + pub fn clear_ipv6(&self, machine_registry_inner: &mut MachineRegistryInner) { + let mut inner = self.inner.lock(); + let Some(ipv6) = inner.ipv6.as_mut() else { + return; + }; + + let mut address_machines = HashMap::>::new(); + for (k, v) in &ipv6.machine_addresses { + address_machines.entry(*v).or_default().push(*k); + } + for (machine_id, addresses) in address_machines { + let machine_state = machine_registry_inner + .machine_states() + .get_state(machine_id) + .expect("must exist"); + + let interfaces = machine_state + .attached_network_interfaces(self.id()) + .expect("must exist"); + for address in addresses { + for interface in &interfaces { + machine_state + .release_address(machine_registry_inner, interface, IpAddr::V6(address)) + .expect("must succeed"); + } + } + } + } pub fn set_ipv6( &self, params: NetworkStateIpv6Params, diff --git a/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs b/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs index 6b25e99f..75753051 100644 --- a/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs +++ b/veilid-tools/src/virtual_network/router_server/machine_registry/state/template_state.rs @@ -289,7 +289,8 @@ impl TemplateState { machine_state.set_bootstrap(false); // Make the default route interface - let vin0 = machine_state.allocate_interface(network_state.id(), None, None)?; + let vin0 = machine_state.allocate_interface(None, None)?; + machine_state.attach_network(machine_registry_inner, &vin0, network_state.id())?; if network_state.is_ipv4() { machine_state.allocate_address_ipv4(machine_registry_inner, &vin0, None, None)?; }