[skip ci] correct addresspool implementation

This commit is contained in:
Christien Rioux 2024-12-19 11:04:14 -05:00
parent f09b367d05
commit 657b66815d
8 changed files with 554 additions and 94 deletions

View File

@ -128,22 +128,22 @@ blueprints:
# with both ipv4 and ipv6 networking # with both ipv4 and ipv6 networking
direct: direct:
ipv4: ipv4:
additional_prefix: 24 prefix: 24
ipv6: ipv6:
additional_prefix: 64 prefix: 64
# An ipv4-only subnet of the internet directly attached with no translation # An ipv4-only subnet of the internet directly attached with no translation
direct_ipv4_no_ipv6: direct_ipv4_no_ipv6:
ipv4: ipv4:
additional_prefix: 24 prefix: 24
# An ipv6-only subnet of the internet directly attached with no translation # An ipv6-only subnet of the internet directly attached with no translation
direct_ipv6_no_ipv4: direct_ipv6_no_ipv4:
ipv6: ipv6:
additional_prefix: 64 prefix: 64
# An ipv4-only subnet of the internet attached via NAT # An ipv4-only subnet of the internet attached via NAT
nat_ipv4_no_ipv6: nat_ipv4_no_ipv6:
ipv4: ipv4:
allocation: "$private" allocation: "$private"
additional_prefix: 0 prefix: 24
gateway: gateway:
translation: "port_restricted" translation: "port_restricted"
upnp: 0.25 upnp: 0.25
@ -152,12 +152,12 @@ blueprints:
nat_ipv4_direct_ipv6: nat_ipv4_direct_ipv6:
ipv4: ipv4:
allocation: "$private" allocation: "$private"
additional_prefix: 0 prefix: 24
gateway: gateway:
translation: "port_restricted" translation: "port_restricted"
upnp: 0.25 upnp: 0.25
ipv6: ipv6:
additional_prefix: 56 prefix: 56
################################################################# #################################################################
# Allocations # Allocations

View File

@ -4,6 +4,10 @@ use ipnet::*;
#[derive(Debug)] #[derive(Debug)]
pub struct AddressPool { pub struct AddressPool {
srng: StableRng, srng: StableRng,
scope_v4: Vec<Ipv4Net>,
scope_v6: Vec<Ipv6Net>,
allocated_v4: Vec<Ipv4Net>, allocated_v4: Vec<Ipv4Net>,
allocated_v6: Vec<Ipv6Net>, allocated_v6: Vec<Ipv6Net>,
} }
@ -12,90 +16,349 @@ impl AddressPool {
pub fn new(srng: StableRng) -> Self { pub fn new(srng: StableRng) -> Self {
Self { Self {
srng, srng,
scope_v4: Vec::new(),
scope_v6: Vec::new(),
allocated_v4: Vec::new(), allocated_v4: Vec::new(),
allocated_v6: 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; return true;
} }
} }
false false
} }
pub fn contains_v6(&self, allocation: &Ipv6Net) -> bool { pub fn is_in_scope_v6(&self, allocation: Ipv6Net) -> bool {
for x in &self.allocated_v6 { for x in &self.scope_v6 {
if x.contains(allocation) { if x.contains(&allocation) {
return true; return true;
} }
} }
false 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.push(allocation);
self.allocated_v4 = Ipv4Net::aggregate(&self.allocated_v4); 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.push(allocation);
self.allocated_v6 = Ipv6Net::aggregate(&self.allocated_v6); self.allocated_v6 = Ipv6Net::aggregate(&self.allocated_v6);
Ok(())
} }
pub fn add_random_subnet_v4( pub fn get_overlaps_v4(&self, allocation: Ipv4Net) -> Vec<Ipv4Net> {
&mut self, let mut overlaps = Vec::<Ipv4Net>::new();
allocation: &Ipv4Net, for x in &self.allocated_v4 {
additional_prefix: u8, if x.contains(&allocation) || allocation.contains(x) {
) -> Option<Ipv4Net> { overlaps.push(*x);
// Apply the additional prefix overlaps = Ipv4Net::aggregate(&overlaps);
let prefix = u8::max( }
allocation.prefix_len() + additional_prefix, }
allocation.max_prefix_len(), overlaps
}
pub fn get_overlaps_v6(&self, allocation: Ipv6Net) -> Vec<Ipv6Net> {
let mut overlaps = Vec::<Ipv6Net>::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<Ipv4Net> {
// 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"
); );
// Get the subnets with this prefix let scope_end_subnet_index = scope_start_subnet_index + scope_ranges[scope_index].2;
let mut subnets = allocation.subnets(prefix).ok()?.collect::<Vec<Ipv4Net>>(); if chosen_subnet_index < scope_end_subnet_index {
break;
// 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);
} }
// 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 None
} }
pub fn add_random_subnet_v6( pub fn allocate_random_v6(&mut self, prefix: u8) -> Option<Ipv6Net> {
&mut self, // Scope ranges to iterate
allocation: &Ipv6Net, let mut scope_ranges = Vec::<(Ipv6Net, u8, u128)>::new();
additional_prefix: u8, let mut total_subnets = 0u128;
) -> Option<Ipv6Net> {
// Apply the additional prefix // Build range set from scopes, minus the prefix to allocate
let prefix = u8::max( for scope in self.scope_v6.iter().copied() {
allocation.prefix_len() + additional_prefix, // If the prefix we are looking to allocate doesn't fit in this scope
allocation.max_prefix_len(), // 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"
); );
// Get the subnets with this prefix let scope_end_subnet_index = scope_start_subnet_index + scope_ranges[scope_index].2;
let mut subnets = allocation.subnets(prefix).ok()?.collect::<Vec<Ipv6Net>>(); if chosen_subnet_index < scope_end_subnet_index {
break;
// 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);
} }
// 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 None
} }
} }

View File

@ -32,6 +32,9 @@ impl MachineRegistryInner {
pub fn srng(&self) -> StableRng { pub fn srng(&self) -> StableRng {
self.unlocked_inner.srng.clone() self.unlocked_inner.srng.clone()
} }
pub fn config(&self) -> &config::Config {
&self.unlocked_inner.config
}
pub fn execute_config(&self, cfg: config::Config) -> MachineRegistryResult<()> { pub fn execute_config(&self, cfg: config::Config) -> MachineRegistryResult<()> {
// Create all networks // Create all networks
@ -479,7 +482,7 @@ impl MachineRegistryInner {
// pub(super) fn choose_allocation_v4( // pub(super) fn choose_allocation_v4(
// &mut self, // &mut self,
// allocation: config::Allocation, // allocation: config::Allocation,
// additional_prefix: u8, // prefix: u8,
// ) -> MachineRegistryResult<Ipv4Net> { // ) -> MachineRegistryResult<Ipv4Net> {
// // Get allocation subnet candidates // // Get allocation subnet candidates
// let mut subnet4 = allocation // let mut subnet4 = allocation
@ -495,7 +498,7 @@ impl MachineRegistryInner {
// // Allocate within the subnet // // Allocate within the subnet
// match self // match self
// .address_pool // .address_pool
// .add_random_subnet_v4(subnet, additional_prefix) // .add_random_subnet_v4(subnet, prefix)
// { // {
// Some(a) => { // Some(a) => {
// // Got a sub-allocation // // Got a sub-allocation
@ -520,7 +523,7 @@ impl MachineRegistryInner {
// pub(super) fn choose_allocation_v6( // pub(super) fn choose_allocation_v6(
// &mut self, // &mut self,
// allocation: config::Allocation, // allocation: config::Allocation,
// additional_prefix: u8, // prefix: u8,
// ) -> MachineRegistryResult<Ipv6Net> { // ) -> MachineRegistryResult<Ipv6Net> {
// // Get allocation subnet candidates // // Get allocation subnet candidates
// let mut subnet6 = allocation // let mut subnet6 = allocation
@ -536,7 +539,7 @@ impl MachineRegistryInner {
// // Allocate within the subnet // // Allocate within the subnet
// match self // match self
// .address_pool // .address_pool
// .add_random_subnet_v6(subnet, additional_prefix) // .add_random_subnet_v6(subnet, prefix)
// { // {
// Some(a) => { // Some(a) => {
// // Got a sub-allocation // // Got a sub-allocation

View File

@ -33,6 +33,7 @@ pub enum MachineRegistryError {
NetworkNotFound, NetworkNotFound,
TemplateNotFound, TemplateNotFound,
BlueprintNotFound, BlueprintNotFound,
ModelNotFound,
NoAllocation, NoAllocation,
} }

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use ipnet::*;
#[derive(Debug)] #[derive(Debug)]
struct BlueprintStateUnlockedInner { struct BlueprintStateUnlockedInner {
@ -9,14 +10,14 @@ struct BlueprintStateUnlockedInner {
#[derive(Debug)] #[derive(Debug)]
pub struct BlueprintStateIpv4Params { pub struct BlueprintStateIpv4Params {
pub allocation: Option<String>, pub allocation: Option<String>,
pub additional_prefix: u8, pub prefix: u8,
pub gateway: Option<BlueprintStateGatewayParams>, pub gateway: Option<BlueprintStateGatewayParams>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct BlueprintStateIpv6Params { pub struct BlueprintStateIpv6Params {
pub allocation: Option<String>, pub allocation: Option<String>,
pub additional_prefix: u8, pub prefix: u8,
pub gateway: Option<BlueprintStateGatewayParams>, pub gateway: Option<BlueprintStateGatewayParams>,
} }
@ -170,7 +171,24 @@ impl BlueprintState {
machine_registry_inner: &mut MachineRegistryInner, machine_registry_inner: &mut MachineRegistryInner,
network_state: NetworkState, network_state: NetworkState,
) -> MachineRegistryResult<()> { ) -> 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( fn generate_ipv4_inner(
@ -178,7 +196,49 @@ impl BlueprintState {
machine_registry_inner: &mut MachineRegistryInner, machine_registry_inner: &mut MachineRegistryInner,
network_state: NetworkState, network_state: NetworkState,
) -> MachineRegistryResult<()> { ) -> 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( fn generate_ipv6_inner(

View File

@ -15,7 +15,7 @@ struct MachineStateInner {
#[derive(Debug)] #[derive(Debug)]
pub struct MachineStateInterface { pub struct MachineStateInterface {
/// The network this interface belongs to /// The network this interface belongs to
pub network_id: NetworkStateId, pub network_id: Option<NetworkStateId>,
/// The veilid NetworkInterface state /// The veilid NetworkInterface state
pub network_interface: NetworkInterface, pub network_interface: NetworkInterface,
} }
@ -94,7 +94,6 @@ impl MachineState {
pub fn allocate_interface( pub fn allocate_interface(
&self, &self,
network_id: NetworkStateId,
opt_name: Option<String>, opt_name: Option<String>,
opt_interface_flags: Option<InterfaceFlags>, opt_interface_flags: Option<InterfaceFlags>,
) -> MachineRegistryResult<String> { ) -> MachineRegistryResult<String> {
@ -112,7 +111,7 @@ impl MachineState {
inner.interfaces.insert( inner.interfaces.insert(
name.clone(), name.clone(),
MachineStateInterface { MachineStateInterface {
network_id, network_id: None,
network_interface: NetworkInterface { network_interface: NetworkInterface {
name: name.clone(), name: name.clone(),
flags, flags,
@ -143,9 +142,12 @@ impl MachineState {
}; };
// Get the network state // Get the network state
let Some(network_id) = intf.network_id else {
return Err(MachineRegistryError::NetworkNotFound);
};
let network_state = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)?; .get_state(network_id)?;
// Allocate interface address // Allocate interface address
let is_dynamic = opt_address.is_none(); let is_dynamic = opt_address.is_none();
@ -180,9 +182,12 @@ impl MachineState {
}; };
// Get the network state // Get the network state
let Some(network_id) = intf.network_id else {
return Err(MachineRegistryError::NetworkNotFound);
};
let network_state = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)?; .get_state(network_id)?;
// Allocate interface address // Allocate interface address
let is_dynamic = opt_address.is_none(); let is_dynamic = opt_address.is_none();
@ -204,6 +209,47 @@ impl MachineState {
Ok(ifv6_addr) 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<Vec<String>> {
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( pub fn release_address(
&self, &self,
machine_registry_inner: &mut MachineRegistryInner, machine_registry_inner: &mut MachineRegistryInner,
@ -215,10 +261,14 @@ impl MachineState {
return Err(MachineRegistryError::InvalidName); return Err(MachineRegistryError::InvalidName);
}; };
let Some(network_id) = intf.network_id else {
return Err(MachineRegistryError::NetworkNotFound);
};
// Get the network state // Get the network state
let network_state = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)?; .get_state(network_id)?;
// Release the address from the network // Release the address from the network
match address { match address {
@ -243,6 +293,19 @@ impl MachineState {
Self::release_all_addresses_inner(&mut *inner, machine_registry_inner, interface) 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( fn release_all_addresses_inner(
inner: &mut MachineStateInner, inner: &mut MachineStateInner,
machine_registry_inner: &mut MachineRegistryInner, machine_registry_inner: &mut MachineRegistryInner,
@ -252,10 +315,14 @@ impl MachineState {
return Err(MachineRegistryError::InvalidName); return Err(MachineRegistryError::InvalidName);
}; };
let Some(network_id) = intf.network_id else {
return Ok(());
};
// Get the network state // Get the network state
let network_state = machine_registry_inner let network_state = machine_registry_inner
.network_states() .network_states()
.get_state(intf.network_id)?; .get_state(network_id)?;
// Release the addresses from the network // Release the addresses from the network
for addr in &intf.network_interface.addrs { for addr in &intf.network_interface.addrs {
@ -277,7 +344,7 @@ impl MachineState {
interface: &str, interface: &str,
) -> MachineRegistryResult<()> { ) -> MachineRegistryResult<()> {
let mut inner = self.inner.lock(); 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 inner
.interfaces .interfaces
.remove(interface) .remove(interface)

View File

@ -23,17 +23,22 @@ struct NetworkStateInner {
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateModel { struct NetworkStateModel {
/// Network latency distribution params: NetworkStateModelParams,
latency: Option<config::Distribution>,
/// Distance simulation metric
distance: Option<config::Distance>,
/// Packet loss probability
loss: Probability,
} }
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateIpv4Params { pub struct NetworkStateModelParams {
allocation: Ipv4Net, /// Network latency distribution
pub latency: Option<config::Distribution>,
/// Distance simulation metric
pub distance: Option<config::Distance>,
/// Packet loss probability
pub loss: Probability,
}
#[derive(Debug)]
pub struct NetworkStateIpv4Params {
pub allocation: Ipv4Net,
} }
#[derive(Debug)] #[derive(Debug)]
@ -44,8 +49,8 @@ struct NetworkStateIpv4 {
} }
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateIpv6Params { pub struct NetworkStateIpv6Params {
allocation: Ipv6Net, pub allocation: Ipv6Net,
} }
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateIpv6 { struct NetworkStateIpv6 {
@ -55,10 +60,10 @@ struct NetworkStateIpv6 {
} }
#[derive(Debug)] #[derive(Debug)]
struct NetworkStateGatewayParams { pub struct NetworkStateGatewayParams {
translation: config::Translation, pub translation: config::Translation,
upnp: bool, pub upnp: bool,
network: Option<NetworkStateId>, pub network: Option<NetworkStateId>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -88,10 +93,12 @@ impl NetworkState {
inner: Arc::new(Mutex::new(NetworkStateInner { inner: Arc::new(Mutex::new(NetworkStateInner {
generating_blueprint: None, generating_blueprint: None,
model: NetworkStateModel { model: NetworkStateModel {
params: NetworkStateModelParams {
latency: None, latency: None,
distance: None, distance: None,
loss: 0.0, loss: 0.0,
}, },
},
ipv4: None, ipv4: None,
ipv6: 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(); 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::<MachineStateId, Vec<Ipv4Addr>>::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( 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::<MachineStateId, Vec<Ipv6Addr>>::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( pub fn set_ipv6(
&self, &self,
params: NetworkStateIpv6Params, params: NetworkStateIpv6Params,

View File

@ -289,7 +289,8 @@ impl TemplateState {
machine_state.set_bootstrap(false); machine_state.set_bootstrap(false);
// Make the default route interface // 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() { if network_state.is_ipv4() {
machine_state.allocate_address_ipv4(machine_registry_inner, &vin0, None, None)?; machine_state.allocate_address_ipv4(machine_registry_inner, &vin0, None, None)?;
} }