diff --git a/veilid-tools/src/bin/virtual_router/main.rs b/veilid-tools/src/bin/virtual_router/main.rs index 56c045f5..36f29dc7 100644 --- a/veilid-tools/src/bin/virtual_router/main.rs +++ b/veilid-tools/src/bin/virtual_router/main.rs @@ -80,9 +80,9 @@ fn main() -> Result<(), String> { let router_server = virtual_network::RouterServer::new(); - if let Err(e) = router_server.execute_config(initial_config) { - xxx continue here - } + router_server + .execute_config(initial_config) + .map_err(|e| format!("Error executing config: {}", e))?; let _ss_tcp = if !args.no_tcp { Some( diff --git a/veilid-tools/src/virtual_network/router_server/config.rs b/veilid-tools/src/virtual_network/router_server/config.rs index 69becf5f..13cb2f87 100644 --- a/veilid-tools/src/virtual_network/router_server/config.rs +++ b/veilid-tools/src/virtual_network/router_server/config.rs @@ -120,10 +120,10 @@ pub struct TemplateLimits { /// maximum number of machines this template will generate #[validate(nested)] #[serde(default)] - pub machine_count: Option>, + pub machine_count: Option>, #[validate(nested)] #[serde(default)] - pub machines_per_network: Option>, + pub machines_per_network: Option>, } fn validate_template_limits(limits: &TemplateLimits) -> Result<(), ValidationError> { @@ -255,7 +255,7 @@ pub struct BlueprintLimits { /// maximum number of networks this blueprint will generate #[validate(nested)] #[serde(default)] - pub network_count: Option>, + pub network_count: Option>, } fn validate_blueprint_limits(limits: &BlueprintLimits) -> Result<(), ValidationError> { @@ -329,7 +329,7 @@ fn validate_blueprint_ipv4(blueprint_ipv4: &BlueprintIpv4) -> Result<(), Validat pub struct BlueprintIpv6 { #[serde(flatten)] #[validate(nested)] - pub allocation: BlueprintLocation, + pub location: BlueprintLocation, #[validate(nested)] pub prefix: WeightedList, #[serde(default)] @@ -354,29 +354,27 @@ pub struct BlueprintGateway { pub translation: WeightedList, #[validate(range(min = 0.0, max = 1.0))] pub upnp: Probability, - #[serde(flatten)] - pub location: TemplateLocation, + #[serde(default, flatten)] + #[validate(nested)] + pub location: Option, } //////////////////////////////////////////////////////////////// #[derive(Debug, Clone, Serialize, Deserialize, Validate)] -#[validate(schema(function = "validate_subnets"))] -pub struct Subnets { +pub struct Scope4 { + #[validate(length(min = 1))] + pub scope4: Vec, #[serde(default)] - #[validate(nested)] - pub subnet4: Option>, - #[serde(default)] - #[validate(nested)] - pub subnet6: Option>, + pub pool4: Option, } -fn validate_subnets(subnets: &Subnets) -> Result<(), ValidationError> { - if subnets.subnet4.is_none() && subnets.subnet6.is_none() { - return Err(ValidationError::new("badsub") - .with_message("subnets must support at least one address type".into())); - } - Ok(()) +#[derive(Debug, Clone, Serialize, Deserialize, Validate)] +pub struct Scope6 { + #[validate(length(min = 1))] + pub scope6: Vec, + #[serde(default)] + pub pool6: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Validate)] @@ -398,7 +396,7 @@ fn validate_distance(distance: &Distance) -> Result<(), ValidationError> { Ok(()) } -#[derive(Debug, Clone, Serialize, Deserialize, Validate)] +#[derive(Debug, Clone, Serialize, Deserialize, Validate, Default)] #[validate(schema(function = "validate_distribution"))] pub struct Distribution { pub mean: f32, @@ -455,7 +453,10 @@ pub struct Model { pub struct Allocation { #[serde(flatten)] #[validate(nested)] - pub subnets: Subnets, + pub scope4: Option, + #[serde(flatten)] + #[validate(nested)] + pub scope6: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -467,6 +468,8 @@ pub struct Config { #[serde(default)] pub default_model: Option, #[serde(default)] + pub default_pool: Option, + #[serde(default)] pub profiles: HashMap, #[serde(default)] pub machines: HashMap, @@ -509,6 +512,17 @@ impl Validate for Config { } } + if let Some(default_pool) = self.default_pool.as_ref() { + if default_pool.is_empty() { + errors.add( + "default_pool", + ValidationError::new("badlen").with_message( + "Config must have non-empty default pool if specified".into(), + ), + ); + } + } + errors.merge_self("profiles", validate_hash_map(&self.profiles)); errors.merge_self("machines", validate_hash_map(&self.machines)); errors.merge_self("templates", validate_hash_map(&self.templates)); @@ -575,6 +589,7 @@ impl Config { seed: other.seed.or(self.seed), default_network: other.default_network.or(self.default_network), default_model: other.default_model.or(self.default_model), + default_pool: other.default_pool.or(self.default_pool), profiles: self.profiles.into_iter().chain(other.profiles).collect(), machines: self.machines.into_iter().chain(other.machines).collect(), templates: self.templates.into_iter().chain(other.templates).collect(), 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 7696f247..a798c15f 100644 --- a/veilid-tools/src/virtual_network/router_server/default_config.yml +++ b/veilid-tools/src/virtual_network/router_server/default_config.yml @@ -14,6 +14,9 @@ # this is '$lan') # default_model: "$lan" +# The name of the default allocation pool that subnets are allocated from +# default_pool: "$internet" + ################################################################# # Profiles # @@ -174,56 +177,58 @@ blueprints: allocations: # Custom network allocations boot: - subnet4: ["170.64.128.0/24"] - subnet6: ["2a03:b0c0:2::/48"] - # # Predefined networks - # $internet: {} + scope4: ["170.64.128.0/24"] + scope6: ["2a03:b0c0:2::/48"] + # # Predefined allocations + # $internet: + # scope4: ["0.0.0.0/0"] + # scope6: ["::/0"] # $private: - # subnet4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] - # subnet6: ["fc00::/7"] + # scope4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] + # scope6: ["fc00::/7"] # $cgnat: - # subnet4: ["100.64.0.0/10"] + # scope4: ["100.64.0.0/10"] # $linklocal: - # subnet4: ["169.254.0.0/16"] - # subnet6: ["fe80::/10"] + # scope4: ["169.254.0.0/16"] + # scope6: ["fe80::/10"] # $localhost: - # subnet4: ["127.0.0.0/8"] - # subnet6: ["::1/128"] + # scope4: ["127.0.0.0/8"] + # scope6: ["::1/128"] # $ietf: - # subnet4: ["192.0.0.0/24"] + # scope4: ["192.0.0.0/24"] # $cellnat: - # subnet4: ["192.0.0.0/29"] + # scope4: ["192.0.0.0/29"] # $documentation: - # subnet4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"] - # subnet6: ["2001:db8::/32", "3fff::/20"] + # scope4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"] + # scope6: ["2001:db8::/32", "3fff::/20"] # $benchmark: - # subnet4: ["198.18.0.0/15"] + # scope4: ["198.18.0.0/15"] # $mulitcast: - # subnet4: ["224.0.0.0/4"] + # scope4: ["224.0.0.0/4"] # $mulitcasttest: - # subnet4: ["233.252.0.0/24"] - # subnet6: ["ff00::/8"] + # scope4: ["233.252.0.0/24"] + # scope6: ["ff00::/8"] # $unspecified: - # subnet4: ["0.0.0.0/8"] - # subnet6: ["::/128"] + # scope4: ["0.0.0.0/8"] + # scope6: ["::/128"] # $reserved: - # subnet4: ["192.88.99.0/24", "240.0.0.0/4"] + # scope4: ["192.88.99.0/24", "240.0.0.0/4"] # $broadcast: - # subnet4: ["255.255.255.255/32"] + # scope4: ["255.255.255.255/32"] # $mapped: - # subnet6: ["::ffff:0:0/96", "::ffff:0:0:0/96"] + # scope6: ["::ffff:0:0/96", "::ffff:0:0:0/96"] # $translation: - # subnet6: ["64:ff9b::/96", "64:ff9b:1::/48"] + # scope6: ["64:ff9b::/96", "64:ff9b:1::/48"] # $discard: - # subnet6: ["100::/64"] + # scope6: ["100::/64"] # $teredo: - # subnet6: ["2001::/32"] + # scope6: ["2001::/32"] # $orchidv2: - # subnet6: ["2001:20::/28"] + # scope6: ["2001:20::/28"] # $6to4: - # subnet6: ["2002::/16"] + # scope6: ["2002::/16"] # $srv6: - # subnet6: ["5f00::/16"] + # scope6: ["5f00::/16"] ################################################################# # Models # diff --git a/veilid-tools/src/virtual_network/router_server/global_state_manager/address_pool.rs b/veilid-tools/src/virtual_network/router_server/global_state_manager/address_pool.rs index 5965bb8a..8a3e6a3d 100644 --- a/veilid-tools/src/virtual_network/router_server/global_state_manager/address_pool.rs +++ b/veilid-tools/src/virtual_network/router_server/global_state_manager/address_pool.rs @@ -26,6 +26,19 @@ impl AddressPool { ///////////////////////////////////////////////////////////////////// + pub fn scopes_v4(&self) -> Vec { + self.scope_v4.iter().cloned().collect() + } + pub fn allocations_v4(&self) -> Vec { + self.allocated_v4.iter().cloned().collect() + } + pub fn scopes_v6(&self) -> Vec { + self.scope_v6.iter().cloned().collect() + } + pub fn allocations_v6(&self) -> Vec { + self.allocated_v6.iter().cloned().collect() + } + pub fn add_scope_v4(&mut self, allocation: Ipv4Net) { let mut scopes = self.scope_v4.iter().copied().collect::>(); scopes.push(allocation); @@ -40,22 +53,22 @@ impl AddressPool { self.scope_v6 = scopes.into(); } - pub fn is_in_scope_v4(&self, allocation: Ipv4Net) -> bool { + pub fn find_scope_v4(&self, allocation: Ipv4Net) -> Option { for x in &self.scope_v4 { if x.contains(&allocation) { - return true; + return Some(*x); } } - false + None } - pub fn is_in_scope_v6(&self, allocation: Ipv6Net) -> bool { + pub fn find_scope_v6(&self, allocation: Ipv6Net) -> Option { for x in &self.scope_v6 { if x.contains(&allocation) { - return true; + return Some(*x); } } - false + None } pub fn can_allocate_v6(&self, prefix: u8) -> GlobalStateManagerResult { @@ -82,11 +95,11 @@ impl AddressPool { &mut self, allocation: Ipv4Net, opt_tag: Option, - ) -> GlobalStateManagerResult<()> { + ) -> GlobalStateManagerResult { // Ensure the allocation is in our scope - if !self.is_in_scope_v4(allocation) { + let Some(scope) = self.find_scope_v4(allocation) else { return Err(GlobalStateManagerError::NoAllocation); - } + }; // Only reserve if it's not overlapping an allocation if !self.get_overlaps_v4(allocation).is_empty() { @@ -97,18 +110,18 @@ impl AddressPool { self.allocated_v4.insert_ord(allocation); self.owner_tags_v4.insert(allocation, opt_tag); - Ok(()) + Ok(scope) } pub fn reserve_allocation_v6( &mut self, allocation: Ipv6Net, opt_tag: Option, - ) -> GlobalStateManagerResult<()> { + ) -> GlobalStateManagerResult { // Ensure the allocation is in our scope - if !self.is_in_scope_v6(allocation) { + let Some(scope) = self.find_scope_v6(allocation) else { return Err(GlobalStateManagerError::NoAllocation); - } + }; // Only reserve if it's not overlapping an allocation if !self.get_overlaps_v6(allocation).is_empty() { @@ -119,7 +132,7 @@ impl AddressPool { self.allocated_v6.insert_ord(allocation); self.owner_tags_v6.insert(allocation, opt_tag); - Ok(()) + Ok(scope) } pub fn get_overlaps_v4(&self, allocation: Ipv4Net) -> Vec { diff --git a/veilid-tools/src/virtual_network/router_server/global_state_manager/global_state_manager_inner.rs b/veilid-tools/src/virtual_network/router_server/global_state_manager/global_state_manager_inner.rs index 22af1c14..d52951bb 100644 --- a/veilid-tools/src/virtual_network/router_server/global_state_manager/global_state_manager_inner.rs +++ b/veilid-tools/src/virtual_network/router_server/global_state_manager/global_state_manager_inner.rs @@ -1,11 +1,20 @@ use super::*; +#[derive(Debug, Clone)] +pub(super) struct Allocation { + pub config: config::Allocation, + pub address_pool: AddressPool<()>, +} + #[derive(Debug, Clone)] pub(super) struct GlobalStateManagerInner { unlocked_inner: Arc, srng: StableRng, + default_network: Option, + default_model: Option, + default_pool: Option, models: imbl::HashMap, - allocations: imbl::HashMap, + allocations: imbl::HashMap>, allocated_machines: imbl::HashSet, profile_state_registry: StateRegistry, machine_state_registry: StateRegistry, @@ -22,6 +31,9 @@ impl GlobalStateManagerInner { GlobalStateManagerInner { unlocked_inner, srng: StableRng::new(0), + default_network: None, + default_model: None, + default_pool: None, models: imbl::HashMap::new(), allocations: imbl::HashMap::new(), allocated_machines: imbl::HashSet::new(), @@ -39,27 +51,89 @@ impl GlobalStateManagerInner { self.srng = StableRng::new(seed); } - // Import all allocation definitions - for a in cfg.allocations { - if self.allocations.contains_key(&a.0) { - return Err(GlobalStateManagerError::DuplicateName(a.0)); - } - self.allocations.insert(a.0, a.1); + // Set default network name + if let Some(default_network) = cfg.default_network { + self.default_network = Some(default_network); } + // Set default model name + if let Some(default_model) = cfg.default_model { + self.default_model = Some(default_model); + } + + // Set default pool name + if let Some(default_pool) = cfg.default_pool { + self.default_pool = Some(default_pool); + } + + // Import all allocation definitions + self.execute_config_allocations(&cfg.allocations)?; + // Import all models - for m in cfg.models { - if self.models.contains_key(&m.0) { - return Err(GlobalStateManagerError::DuplicateName(m.0)); - } - self.models.insert(m.0, m.1); + for (name, model) in cfg.models { + self.execute_config_model(name, model)?; } // Create all profile states + for (name, profile) in cfg.profiles { + self.execute_config_profile(name, profile)?; + } // Create all network states + // Don't process gateways yet because they will depend on networks existing + for (name, network) in cfg.networks.clone() { + self.execute_config_network(name, network)?; + } + // Process all ipv4 and ipv6 configurations + for (name, network) in cfg.networks.clone() { + if let Some(ipv4) = network.ipv4.as_ref() { + self.execute_config_network_ipv4(name.clone(), ipv4)?; + } + if let Some(ipv6) = network.ipv6.as_ref() { + self.execute_config_network_ipv6(name.clone(), ipv6)?; + } + } + // Process all network gateways + for (name, network) in cfg.networks { + if let Some(ipv4) = network.ipv4.as_ref() { + if let Some(ipv4gw) = ipv4.gateway.as_ref() { + self.execute_config_network_ipv4_gateway(name.clone(), ipv4gw)?; + } + } + if let Some(ipv6) = network.ipv6.as_ref() { + if let Some(ipv6gw) = ipv6.gateway.as_ref() { + self.execute_config_network_ipv6_gateway(name.clone(), ipv6gw)?; + } + } + } // Create all blueprint states + // Don't process gateways yet because they will depend on blueprints existing + for (name, blueprint) in cfg.blueprints.clone() { + self.execute_config_blueprint(name, blueprint)?; + } + // Process all ipv4 and ipv6 configurations + for (name, blueprint) in cfg.blueprints.clone() { + if let Some(ipv4) = blueprint.ipv4.as_ref() { + self.execute_config_blueprint_ipv4(name.clone(), ipv4)?; + } + if let Some(ipv6) = blueprint.ipv6.as_ref() { + self.execute_config_blueprint_ipv6(name.clone(), ipv6)?; + } + } + // Process all blueprint gateways + for (name, blueprint) in cfg.blueprints { + if let Some(ipv4) = blueprint.ipv4.as_ref() { + if let Some(ipv4gw) = ipv4.gateway.as_ref() { + self.execute_config_blueprint_ipv4_gateway(name.clone(), ipv4gw)?; + } + } + if let Some(ipv6) = blueprint.ipv6.as_ref() { + if let Some(ipv6gw) = ipv6.gateway.as_ref() { + self.execute_config_blueprint_ipv6_gateway(name.clone(), ipv6gw)?; + } + } + } // Create all template states @@ -179,10 +253,44 @@ impl GlobalStateManagerInner { &mut self.srng } + pub(super) fn or_default_network( + &self, + network: Option, + ) -> GlobalStateManagerResult { + match network { + Some(x) => Ok(x), + None => self + .default_network + .clone() + .ok_or(GlobalStateManagerError::NoDefaultNetwork), + } + } + pub(super) fn or_default_model( + &self, + model: Option, + ) -> GlobalStateManagerResult { + match model { + Some(x) => Ok(x), + None => self + .default_model + .clone() + .ok_or(GlobalStateManagerError::NoDefaultModel), + } + } + pub(super) fn or_default_pool(&self, pool: Option) -> GlobalStateManagerResult { + match pool { + Some(x) => Ok(x), + None => self + .default_pool + .clone() + .ok_or(GlobalStateManagerError::NoDefaultPool), + } + } + pub(super) fn models(&self) -> &imbl::HashMap { &self.models } - pub(super) fn allocations(&self) -> &imbl::HashMap { + pub(super) fn allocations(&self) -> &imbl::HashMap> { &self.allocations } @@ -218,463 +326,580 @@ impl GlobalStateManagerInner { &mut self.blueprint_state_registry } - // pub(super) fn get_or_create_machine_state( - // &mut self, - // opt_name: Option, - // params: config::Machine, - // ) -> MachineRegistryResult { - // // Ensure we don't already have this machine created (name must be unique) - // if let Some(name) = &opt_name { - // if let Some(machine_id) = self.resolve_to_manager_machine.add(name.clone()).get() { - // return Ok(self - // .machine_state_by_id - // .get(&machine_id) - // .cloned() - // .expect("must exist")); - // } - // } + fn execute_config_allocations( + &mut self, + config_allocations: &HashMap, + ) -> GlobalStateManagerResult<()> { + for (allocation_name, allocation_config) in config_allocations { + if self.allocations.contains_key(allocation_name) { + return Err(GlobalStateManagerError::DuplicateName( + allocation_name.clone(), + )); + } + let address_pool = + self.resolve_address_pool(allocation_name.clone(), config_allocations)?; + self.allocations.insert( + allocation_name.clone(), + Arc::new(Allocation { + config: allocation_config.clone(), + address_pool, + }), + ); + } + Ok(()) + } + fn execute_config_model( + &mut self, + name: String, + model: config::Model, + ) -> GlobalStateManagerResult<()> { + if self.models.contains_key(&name) { + return Err(GlobalStateManagerError::DuplicateName(name)); + } + self.models.insert(name, model); + Ok(()) + } - // // Allocate a machine id - // let machine_id = self.free_machine_ids.pop().unwrap_or_else(|| { - // let x = self.next_machine_id; - // self.next_machine_id += 1; - // x - // }); + fn execute_config_profile( + &mut self, + name: String, + profile: config::Profile, + ) -> GlobalStateManagerResult<()> { + if self + .profile_state_registry + .get_state_id_by_name(&name) + .is_some() + { + return Err(GlobalStateManagerError::DuplicateName(name)); + } - // // Create a new machine state - // let machine_state = match MachineState::try_new( - // self, - // machine_id, - // MachineStateName::Machine(name.clone()), - // machine_def.clone(), - // ) { - // Ok(v) => v, - // Err(e) => { - // // Release the machine id - // self.free_machine_ids.push(machine_id); - // return Err(e); - // } - // }; + let id = self.profile_state_registry.allocate_id(); + let state = ProfileState::new(id, name, profile); + self.profile_state_registry + .attach_state(state) + .expect("must attach"); - // // Store the machine state with its unique id - // self.machine_state_by_id.insert(machine_id, machine_state); + Ok(()) + } - // // Bind the name to the id - // self.resolve_to_manager_machine - // .resolve(&name, machine_id) - // .expect("must resolve"); + fn execute_config_network( + &mut self, + name: String, + network: config::Network, + ) -> GlobalStateManagerResult<()> { + if self + .network_state_registry + .get_state_id_by_name(&name) + .is_some() + { + return Err(GlobalStateManagerError::DuplicateName(name)); + } - // // Return the state - // Ok(self - // .machine_state_by_id - // .get(&machine_id) - // .cloned() - // .expect("must exist")) - // } + let id = self.network_state_registry.allocate_id(); + let state = { + let mut network_state = NetworkState::new(id, Some(name), NetworkOrigin::Direct); - // pub(super) fn get_machine_state_by_id( - // &mut self, - // machine_id: MachineId, - // ) -> MachineRegistryResult<&mut MachineState> { - // self.machine_state_by_id - // .get_mut(&machine_id) - // .ok_or_else(|| MachineRegistryError::MachineNotFound) - // } + // Set model + let model_name = self.or_default_model(network.model)?; + let model = self + .models + .get(&model_name) + .ok_or(GlobalStateManagerError::ModelNotFound(model_name))?; + network_state.set_model(NetworkStateModelParams { + latency: model.latency.clone(), + distance: model.distance.clone(), + loss: model.loss, + }); - // pub(super) fn get_or_create_network_state( - // &mut self, - // name: String, - // ) -> MachineRegistryResult { + Ok(network_state) + } + .inspect_err(|_| { + self.network_state_registry + .release_id(id) + .expect("must release"); + })?; + self.network_state_registry + .attach_state(state) + .expect("must attach"); + Ok(()) + } - // // Get the network def from the config - // let Some(network_def) = self.unlocked_inner.config.networks.get(&name).cloned() else { - // return Err(MachineRegistryError::NetworkNotFound); - // }; + fn execute_config_network_ipv4( + &mut self, + name: String, + ipv4: &config::NetworkIpv4, + ) -> GlobalStateManagerResult<()> { + let network_state_id = self + .network_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut network_state = self + .network_state_registry + .get_state(network_state_id) + .expect("must exist"); - // // Ensure we don't already have this network created (name must be unique) - // if let Some(network_id) = self.resolve_to_manager_network.add(name.clone()).get() { - // return Ok(self - // .network_state_by_id - // .get(&network_id) - // .cloned() - // .expect("must exist")); - // } + // Get IPV4 allocation + let address_pool = &self + .allocations + .get(&ipv4.allocation) + .cloned() + .ok_or_else(|| GlobalStateManagerError::AllocationNotFound(ipv4.allocation.clone()))? + .address_pool; + let scope = address_pool.scopes_v4(); + let reserve = address_pool.allocations_v4(); - // // Allocate a network id - // let network_id = self.free_network_ids.pop().unwrap_or_else(|| { - // let x = self.next_network_id; - // self.next_network_id += 1; - // x - // }); + // Set IPV4 config + network_state.set_ipv4( + self, + NetworkStateIpv4Params { + scope, + reserve, + super_net: None, + }, + )?; - // // Create a new network state - // let network_state = match NetworkState::try_new( - // self, - // network_id, - // NetworkStateName::Network(name.clone()), - // network_def.clone(), - // ) { - // Ok(v) => v, - // Err(e) => { - // // Release the network id - // self.free_network_ids.push(network_id); - // return Err(e); - // } - // }; + // Update state + self.network_state_registry.set_state(network_state); - // // Store the network state with its unique id - // self.network_state_by_id.insert(network_id, network_state); + Ok(()) + } - // // Bind the name to the id - // self.resolve_to_manager_network - // .resolve(&name, network_id) - // .expect("must resolve"); + fn execute_config_network_ipv6( + &mut self, + name: String, + ipv6: &config::NetworkIpv6, + ) -> GlobalStateManagerResult<()> { + let network_state_id = self + .network_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut network_state = self + .network_state_registry + .get_state(network_state_id) + .expect("must exist"); - // // Return the unique id - // Ok(self - // .network_state_by_id - // .get(&network_id) - // .cloned() - // .expect("must exist")) - // } + // Get IPV4 allocation + let address_pool = &self + .allocations + .get(&ipv6.allocation) + .cloned() + .ok_or_else(|| GlobalStateManagerError::AllocationNotFound(ipv6.allocation.clone()))? + .address_pool; + let scope = address_pool.scopes_v6(); + let reserve = address_pool.allocations_v6(); - // pub(super) fn get_network_state_by_name(&self, name: &String) -> Option { - // let network_id = self.resolve_to_manager_network.get(name)?; - // self.network_state_by_id.get(&network_id).cloned() - // } + // Set IPV4 config + network_state.set_ipv6( + self, + NetworkStateIpv6Params { + scope, + reserve, + super_net: None, + }, + )?; - // pub(super) fn get_network_state_by_id( - // &self, - // network_id: NetworkId, - // ) -> MachineRegistryResult { - // self.network_state_by_id - // .get(&network_id) - // .cloned() - // .ok_or_else(|| MachineRegistryError::NetworkNotFound) - // } + // Update state + self.network_state_registry.set_state(network_state); - // pub(super) fn get_or_create_template_state( - // &mut self, - // name: &String, - // template_def: config::Template, - // ) -> MachineRegistryResult<&mut TemplateState> { - // // Ensure we don't already have this template created (name must be unique) - // if self.template_state_by_name.contains_key(name) { - // return Ok(self - // .template_state_by_name - // .get_mut(name) - // .expect("must exist")); - // } + Ok(()) + } - // // Create a new template state - // let template_state = match TemplateState::try_new(self, name.clone(), template_def.clone()) - // { - // Ok(v) => v, - // Err(e) => { - // return Err(e); - // } - // }; + fn execute_config_network_ipv4_gateway( + &mut self, + name: String, + ipv4gw: &config::NetworkGateway, + ) -> GlobalStateManagerResult<()> { + let network_state_id = self + .network_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut network_state = self + .network_state_registry + .get_state(network_state_id) + .expect("must exist"); - // // Store the template state with its name - // self.template_state_by_name - // .insert(name.clone(), template_state); - // Ok(self - // .template_state_by_name - // .get_mut(name) - // .expect("must exist")) - // } + let translation = ipv4gw.translation; + let upnp = ipv4gw.upnp; + let external_network_name = self.or_default_network(ipv4gw.network.clone())?; + let external_network = self + .network_state_registry + .get_state_id_by_name(&external_network_name) + .ok_or(GlobalStateManagerError::NetworkNotFound( + external_network_name, + ))?; - // pub(super) fn get_or_create_machine_state_from_template( - // &mut self, - // name: String, - // template_def: config::Template, - // ) -> MachineRegistryResult { - // // Make machine def from current template state - // let machine_def = { - // // Get the active template state - // let template_state = self.get_or_create_template_state(&name, template_def)?; - // if !template_state.is_active(self)? { - // return Err(MachineRegistryError::TemplateComplete); - // } + let gateway_params = NetworkStateIpv4GatewayParams { + translation, + upnp, + external_network, + internal_address: None, + external_address: None, + }; - // // Pick or instantiate an available network - // template_state.generate(self)? + network_state.set_ipv4_gateway(self, gateway_params)?; - // xxx how to pass through per-network limits - // }; + // Update state + self.network_state_registry.set_state(network_state); - // // Allocate a machine id - // let machine_id = self.free_machine_ids.pop().unwrap_or_else(|| { - // let x = self.next_machine_id; - // self.next_machine_id += 1; - // x - // }); + Ok(()) + } - // // Create a new machine state - // let machine_state = match MachineState::try_new( - // self, - // machine_id, - // MachineStateName::Template(name.clone()), - // machine_def.clone(), - // ) { - // Ok(v) => v, - // Err(e) => { - // // Release the machine id - // self.free_machine_ids.push(machine_id); - // return Err(e); - // } - // }; + fn execute_config_network_ipv6_gateway( + &mut self, + name: String, + ipv6gw: &config::NetworkGateway, + ) -> GlobalStateManagerResult<()> { + let network_state_id = self + .network_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut network_state = self + .network_state_registry + .get_state(network_state_id) + .expect("must exist"); - // // Store the machine state with its unique id - // self.machine_state_by_id.insert(machine_id, machine_state); + let translation = ipv6gw.translation; + let upnp = ipv6gw.upnp; + let external_network_name = self.or_default_network(ipv6gw.network.clone())?; + let external_network = self + .network_state_registry + .get_state_id_by_name(&external_network_name) + .ok_or(GlobalStateManagerError::NetworkNotFound( + external_network_name, + ))?; - // // Add to machines for this template - // { - // let template_state = self.get_template_state(&name).expect("must exist"); - // template_state.machines.insert(machine_id); - // } + let gateway_params = NetworkStateIpv4GatewayParams { + translation, + upnp, + external_network, + internal_address: None, + external_address: None, + }; - // // Return the unique id - // Ok(machine_id) - // } - // pub(super) fn get_template_state(&self, name: &String) -> MachineRegistryResult { - // self.template_state_by_name - // .get(name) - // .cloned() - // .ok_or_else(|| MachineRegistryError::TemplateNotFound) - // } + network_state.set_ipv4_gateway(self, gateway_params)?; - // pub(super) fn get_or_create_network_state_from_machine_location( - // &mut self, - // machine_location: &config::MachineLocation, - // ) -> MachineRegistryResult { - // match machine_location { - // config::MachineLocation::Network { - // network: name, - // address4: _, - // address6: _, - // } => { - // let network_def = self - // .unlocked_inner - // .config - // .networks - // .get(name) - // .cloned() - // .expect("config validation is broken"); - // self.get_or_create_network_state(name.clone(), network_def) - // } + // Update state + self.network_state_registry.set_state(network_state); - // config::MachineLocation::Blueprint { blueprint: name } => { - // let blueprint_def = self - // .unlocked_inner - // .config - // .blueprints - // .get(name) - // .cloned() - // .expect("config validation is broken"); - // self.get_or_create_network_state_from_blueprint(name.clone(), blueprint_def) - // } - // } - // } - // pub(super) fn get_or_create_network_state_from_template_location( - // &mut self, - // template_location: &config::TemplateLocation, - // ) -> MachineRegistryResult { - // match template_location { - // config::TemplateLocation::Network { network } => { - // let name = self.unlocked_inner.srng.weighted_choice(network); - // let network_def = self - // .unlocked_inner - // .config - // .networks - // .get(name) - // .cloned() - // .expect("config validation is broken"); - // self.get_or_create_network_state(name.clone(), network_def) - // } - // config::TemplateLocation::Blueprint { blueprint } => { - // let name = self.unlocked_inner.srng.weighted_choice(blueprint); - // let blueprint_def = self - // .unlocked_inner - // .config - // .blueprints - // .get(name) - // .cloned() - // .expect("config validation is broken"); - // self.get_or_create_network_state_from_blueprint(name.clone(), blueprint_def) - // } - // } - // } + Ok(()) + } - // pub(super) fn get_blueprint_state( - // &self, - // name: &String, - // ) -> MachineRegistryResult { - // self.blueprint_state_by_name - // .get(name) - // .cloned() - // .ok_or_else(|| MachineRegistryError::BlueprintNotFound) - // } + fn execute_config_blueprint( + &mut self, + name: String, + blueprint: config::Blueprint, + ) -> GlobalStateManagerResult<()> { + if self + .blueprint_state_registry + .get_state_id_by_name(&name) + .is_some() + { + return Err(GlobalStateManagerError::DuplicateName(name)); + } - // pub(super) fn choose_allocation_v4( - // &mut self, - // allocation: config::Allocation, - // prefix: u8, - // ) -> MachineRegistryResult { - // // Get allocation subnet candidates - // let mut subnet4 = allocation - // .subnets - // .subnet4 - // .clone() - // .ok_or(MachineRegistryError::NoAllocation)?; + let id = self.blueprint_state_registry.allocate_id(); + let state = { + let mut blueprint_state = BlueprintState::new(id, name); - // loop { - // // Pick a compatible subnet from the allocation - // let subnet = self.unlocked_inner.srng.weighted_choice(&subnet4); + // Set model + let model = match blueprint.model { + Some(x) => x, + None => WeightedList::Single( + self.default_model + .clone() + .ok_or(GlobalStateManagerError::NoDefaultModel)?, + ), + }; + blueprint_state.set_model(model); + blueprint_state.set_limit_network_count( + blueprint + .limits + .network_count + .map(|wl| self.srng().weighted_choice(wl)), + ); - // // Allocate within the subnet - // match self - // .address_pool - // .add_random_subnet_v4(subnet, prefix) - // { - // Some(a) => { - // // Got a sub-allocation - // return Ok(a); - // } - // None => { - // // No sub-allocation left in this subnet, - // // remove the subnet so we can choose again - // let Some(next_subnet4) = subnet4.filter(|x| x == subnet) else { - // // No subnets left - // break; - // }; - // subnet4 = next_subnet4; - // } - // } - // } + Ok(blueprint_state) + } + .inspect_err(|_| { + self.blueprint_state_registry + .release_id(id) + .expect("must release"); + })?; + self.blueprint_state_registry + .attach_state(state) + .expect("must attach"); + Ok(()) + } - // // No available allocations left - // Err(MachineRegistryError::NoAllocation) - // } + fn execute_config_blueprint_ipv4( + &mut self, + name: String, + ipv4: &config::BlueprintIpv4, + ) -> GlobalStateManagerResult<()> { + let blueprint_state_id = self + .blueprint_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut blueprint_state = self + .blueprint_state_registry + .get_state(blueprint_state_id) + .expect("must exist"); - // pub(super) fn choose_allocation_v6( - // &mut self, - // allocation: config::Allocation, - // prefix: u8, - // ) -> MachineRegistryResult { - // // Get allocation subnet candidates - // let mut subnet6 = allocation - // .subnets - // .subnet6 - // .clone() - // .ok_or(MachineRegistryError::NoAllocation)?; + let locations = match ipv4.location.clone() { + config::BlueprintLocation::Allocation { allocation } => { + NetworkLocationsList::Allocations { + allocations: allocation, + } + } + config::BlueprintLocation::Network { network } => { + if let Some(network) = network { + let networks = network.try_map(|n| { + self.network_state_registry + .get_state_id_by_name(n) + .ok_or_else(|| GlobalStateManagerError::NetworkNotFound(n.clone())) + })?; + NetworkLocationsList::Networks { networks } + } else { + let default_network = self.or_default_network(None)?; + let default_network_state_id = self + .network_state_registry + .get_state_id_by_name(&default_network) + .ok_or(GlobalStateManagerError::NetworkNotFound(default_network))?; - // loop { - // // Pick a compatible subnet from the allocation - // let subnet = self.unlocked_inner.srng.weighted_choice(&subnet6); + NetworkLocationsList::Networks { + networks: WeightedList::Single(default_network_state_id), + } + } + } + }; - // // Allocate within the subnet - // match self - // .address_pool - // .add_random_subnet_v6(subnet, prefix) - // { - // Some(a) => { - // // Got a sub-allocation - // return Ok(a); - // } - // None => { - // // No sub-allocation left in this subnet, - // // remove the subnet so we can choose again - // let Some(next_subnet6) = subnet6.filter(|x| x == subnet) else { - // // No subnets left - // break; - // }; - // subnet6 = next_subnet6; - // } - // } - // } + let prefix = ipv4.prefix.clone(); - // // No available allocations left - // Err(MachineRegistryError::NoAllocation) - // } + // Set IPV4 config + blueprint_state.set_ipv4( + self, + BlueprintStateIpv4Params { + locations, + prefix, + gateway: None, + }, + )?; - // pub(super) fn get_or_create_blueprint_state( - // &mut self, - // name: &String, - // blueprint_def: config::Blueprint, - // ) -> MachineRegistryResult { - // // Ensure we don't already have this blueprint created (name must be unique) - // if self.blueprint_state_by_name.contains_key(name) { - // return Ok(self - // .blueprint_state_by_name - // .get(name) - // .cloned() - // .expect("must exist")); - // } + // Update state + self.blueprint_state_registry.set_state(blueprint_state); - // // Create a new blueprint state - // let blueprint_state = - // match BlueprintState::try_new(self, name.clone(), blueprint_def.clone()) { - // Ok(v) => v, - // Err(e) => { - // return Err(e); - // } - // }; + Ok(()) + } - // // Store the blueprint state with its name - // self.blueprint_state_by_name - // .insert(name.clone(), blueprint_state); - // Ok(self - // .blueprint_state_by_name - // .get(name) - // .cloned() - // .expect("must exist")) - // } + fn execute_config_blueprint_ipv4_gateway( + &mut self, + name: String, + ipv4gw: &config::BlueprintGateway, + ) -> GlobalStateManagerResult<()> { + let blueprint_state_id = self + .blueprint_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut blueprint_state = self + .blueprint_state_registry + .get_state(blueprint_state_id) + .expect("must exist"); - // pub(super) fn get_or_create_network_state_from_blueprint( - // &mut self, - // name: String, - // blueprint_def: config::Blueprint, - // ) -> MachineRegistryResult { - // // Get the active blueprint state - // let blueprint_state = self.get_or_create_blueprint_state(&name, blueprint_def)?; - // if !blueprint_state.is_active(self)? { - // return Err(MachineRegistryError::BlueprintComplete); - // } + let translation = ipv4gw.translation.clone(); + let upnp = ipv4gw.upnp; + let locations = match ipv4gw.location.clone() { + Some(config::TemplateLocation::Network { network }) => { + let networks = network.try_map(|n| { + self.network_state_registry + .get_state_id_by_name(n) + .ok_or_else(|| GlobalStateManagerError::NetworkNotFound(n.clone())) + })?; + Some(MachineLocationsList::Networks { networks }) + } + Some(config::TemplateLocation::Blueprint { blueprint }) => { + let blueprints = blueprint.try_map(|n| { + self.blueprint_state_registry + .get_state_id_by_name(n) + .ok_or_else(|| GlobalStateManagerError::BlueprintNotFound(n.clone())) + })?; + Some(MachineLocationsList::Blueprints { blueprints }) + } + None => None, + }; - // // Make network def from current blueprint state - // let machine_def = config::Network { - // model: self.unlocked_inner.srng.weighted_choice(blueprint_state), - // ipv4: todo!(), - // ipv6: todo!(), - // }; + let gateway_params = BlueprintStateGatewayParams { + translation, + upnp, + locations, + }; - // // Allocate a machine id - // let machine_id = self.free_machine_ids.pop().unwrap_or_else(|| { - // let x = self.next_machine_id; - // self.next_machine_id += 1; - // x - // }); + blueprint_state.set_ipv4_gateway(self, Some(gateway_params))?; - // // Create a new machine state - // let machine_state = match MachineState::try_new( - // self, - // MachineStateName::Template(name.clone()), - // machine_def.clone(), - // machine_id, - // ) { - // Ok(v) => v, - // Err(e) => { - // // Release the machine id - // self.free_machine_ids.push(machine_id); - // return Err(e); - // } - // }; + // Update state + self.blueprint_state_registry.set_state(blueprint_state); - // // Store the machine state with its unique id - // self.machine_state_by_id.insert(machine_id, machine_state); + Ok(()) + } - // // Return the unique id - // Ok(machine_id) - // } + fn execute_config_blueprint_ipv6( + &mut self, + name: String, + ipv6: &config::BlueprintIpv6, + ) -> GlobalStateManagerResult<()> { + let blueprint_state_id = self + .blueprint_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut blueprint_state = self + .blueprint_state_registry + .get_state(blueprint_state_id) + .expect("must exist"); + + let locations = match ipv6.location.clone() { + config::BlueprintLocation::Allocation { allocation } => { + NetworkLocationsList::Allocations { + allocations: allocation, + } + } + config::BlueprintLocation::Network { network } => { + if let Some(network) = network { + let networks = network.try_map(|n| { + self.network_state_registry + .get_state_id_by_name(n) + .ok_or_else(|| GlobalStateManagerError::NetworkNotFound(n.clone())) + })?; + NetworkLocationsList::Networks { networks } + } else { + let default_network = self.or_default_network(None)?; + let default_network_state_id = self + .network_state_registry + .get_state_id_by_name(&default_network) + .ok_or(GlobalStateManagerError::NetworkNotFound(default_network))?; + + NetworkLocationsList::Networks { + networks: WeightedList::Single(default_network_state_id), + } + } + } + }; + + let prefix = ipv6.prefix.clone(); + + // Set IPV6 config + blueprint_state.set_ipv6( + self, + BlueprintStateIpv6Params { + locations, + prefix, + gateway: None, + }, + )?; + + // Update state + self.blueprint_state_registry.set_state(blueprint_state); + + Ok(()) + } + + fn execute_config_blueprint_ipv6_gateway( + &mut self, + name: String, + ipv6gw: &config::BlueprintGateway, + ) -> GlobalStateManagerResult<()> { + let blueprint_state_id = self + .blueprint_state_registry + .get_state_id_by_name(&name) + .expect("must exist"); + let mut blueprint_state = self + .blueprint_state_registry + .get_state(blueprint_state_id) + .expect("must exist"); + + let translation = ipv6gw.translation.clone(); + let upnp = ipv6gw.upnp; + let locations = match ipv6gw.location.clone() { + Some(config::TemplateLocation::Network { network }) => { + let networks = network.try_map(|n| { + self.network_state_registry + .get_state_id_by_name(n) + .ok_or_else(|| GlobalStateManagerError::NetworkNotFound(n.clone())) + })?; + Some(MachineLocationsList::Networks { networks }) + } + Some(config::TemplateLocation::Blueprint { blueprint }) => { + let blueprints = blueprint.try_map(|n| { + self.blueprint_state_registry + .get_state_id_by_name(n) + .ok_or_else(|| GlobalStateManagerError::BlueprintNotFound(n.clone())) + })?; + Some(MachineLocationsList::Blueprints { blueprints }) + } + None => None, + }; + + let gateway_params = BlueprintStateGatewayParams { + translation, + upnp, + locations, + }; + + blueprint_state.set_ipv6_gateway(self, Some(gateway_params))?; + + // Update state + self.blueprint_state_registry.set_state(blueprint_state); + + Ok(()) + } + + fn resolve_address_pool( + &self, + allocation_name: String, + config_allocations: &HashMap, + ) -> GlobalStateManagerResult> { + // Get the allocation config + let allocation = config_allocations + .get(&allocation_name) + .ok_or_else(|| GlobalStateManagerError::AllocationNotFound(allocation_name.clone()))?; + + // Create an address pool + let mut address_pool = AddressPool::<()>::new(); + + // Apply the scope present in the allocation + if let Some(scope4) = allocation.scope4.as_ref() { + for s in &scope4.scope4 { + address_pool.add_scope_v4(*s); + } + } + if let Some(scope6) = allocation.scope6.as_ref() { + for s in &scope6.scope6 { + address_pool.add_scope_v6(*s); + } + } + + // Reserve out any allocations that used this as their pool + for (k, v) in config_allocations { + // Exclude our own allocation + if *k == allocation_name { + continue; + } + if let Some(scope4) = v.scope4.as_ref() { + let pool = self.or_default_pool(scope4.pool4.clone())?; + if pool == allocation_name { + for s in &scope4.scope4 { + address_pool.reserve_allocation_v4(*s, None)?; + } + } + } + if let Some(scope6) = v.scope6.as_ref() { + let pool = self.or_default_pool(scope6.pool6.clone())?; + if pool == allocation_name { + for s in &scope6.scope6 { + address_pool.reserve_allocation_v6(*s, None)?; + } + } + } + } + + Ok(address_pool) + } } diff --git a/veilid-tools/src/virtual_network/router_server/global_state_manager/mod.rs b/veilid-tools/src/virtual_network/router_server/global_state_manager/mod.rs index 911aaa0d..ec2f42ef 100644 --- a/veilid-tools/src/virtual_network/router_server/global_state_manager/mod.rs +++ b/veilid-tools/src/virtual_network/router_server/global_state_manager/mod.rs @@ -48,14 +48,20 @@ pub enum GlobalStateManagerError { BlueprintNotFound(String), #[error("Model not found: {0}")] ModelNotFound(String), + #[error("Allocation not found: {0}")] + AllocationNotFound(String), #[error("No default model")] NoDefaultModel, #[error("No default network")] NoDefaultNetwork, + #[error("No default pool")] + NoDefaultPool, #[error("No allocation available")] NoAllocation, #[error("Resource in use: {0}")] ResourceInUse(String), + #[error("Invalid gateway")] + InvalidGateway, } pub type GlobalStateManagerResult = Result; diff --git a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/blueprint_state.rs b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/blueprint_state.rs index 313e9111..1a6daf53 100644 --- a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/blueprint_state.rs +++ b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/blueprint_state.rs @@ -69,8 +69,8 @@ pub struct BlueprintState { pub type BlueprintStateId = StateId; impl BlueprintState { - pub fn new(id: BlueprintStateId, name: String) -> GlobalStateManagerResult { - Ok(Self { + pub fn new(id: BlueprintStateId, name: String) -> Self { + Self { immutable: Arc::new(BlueprintStateImmutable { id, name }), fields: Arc::new(BlueprintStateFields { limit_network_count: None, @@ -79,7 +79,7 @@ impl BlueprintState { ipv4: None, ipv6: None, }), - }) + } } pub fn set_limit_network_count(&mut self, limit_network_count: Option) { @@ -99,12 +99,56 @@ impl BlueprintState { }); } + pub fn clear_ipv4( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + ) -> GlobalStateManagerResult<()> { + self.clear_ipv4_gateway(gsm_inner)?; + + if self.fields.ipv4.is_none() { + return Ok(()); + }; + + // Update fields + self.fields = Arc::new(BlueprintStateFields { + ipv4: None, + ..(*self.fields).clone() + }); + + Ok(()) + } + + pub fn clear_ipv4_gateway( + &mut self, + _gsm_inner: &mut GlobalStateManagerInner, + ) -> GlobalStateManagerResult<()> { + let Some(mut ipv4) = self.fields.ipv4.clone() else { + return Ok(()); + }; + let Some(_gateway) = ipv4.gateway else { + return Ok(()); + }; + + // Clear gateway + ipv4.gateway = None; + + // Update fields + self.fields = Arc::new(BlueprintStateFields { + ipv4: Some(ipv4), + ..(*self.fields).clone() + }); + + Ok(()) + } + pub fn set_ipv4( &mut self, + gsm_inner: &mut GlobalStateManagerInner, params: BlueprintStateIpv4Params, - gateway_params: Option, - ) { - let mut ipv4 = if let Some(ipv4) = self.fields.ipv4.clone() { + ) -> GlobalStateManagerResult<()> { + self.clear_ipv4(gsm_inner)?; + + let ipv4 = if let Some(ipv4) = self.fields.ipv4.clone() { BlueprintStateIpv4 { params, ..ipv4 } } else { BlueprintStateIpv4 { @@ -113,6 +157,26 @@ impl BlueprintState { } }; + // Update fields + self.fields = Arc::new(BlueprintStateFields { + ipv4: Some(ipv4), + ..(*self.fields).clone() + }); + + Ok(()) + } + + pub fn set_ipv4_gateway( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + gateway_params: Option, + ) -> GlobalStateManagerResult<()> { + self.clear_ipv4_gateway(gsm_inner)?; + + let Some(mut ipv4) = self.fields.ipv4.clone() else { + return Err(GlobalStateManagerError::InvalidGateway); + }; + if ipv4.gateway.is_some() { if let Some(gateway_params) = gateway_params { ipv4.gateway.as_mut().expect("must exist").params = gateway_params; @@ -130,14 +194,60 @@ impl BlueprintState { ipv4: Some(ipv4), ..(*self.fields).clone() }); + + Ok(()) + } + + pub fn clear_ipv6( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + ) -> GlobalStateManagerResult<()> { + self.clear_ipv6_gateway(gsm_inner)?; + + if self.fields.ipv6.is_none() { + return Ok(()); + }; + + // Update fields + self.fields = Arc::new(BlueprintStateFields { + ipv6: None, + ..(*self.fields).clone() + }); + + Ok(()) + } + + pub fn clear_ipv6_gateway( + &mut self, + _gsm_inner: &mut GlobalStateManagerInner, + ) -> GlobalStateManagerResult<()> { + let Some(mut ipv6) = self.fields.ipv6.clone() else { + return Ok(()); + }; + let Some(_gateway) = ipv6.gateway else { + return Ok(()); + }; + + // Clear gateway + ipv6.gateway = None; + + // Update fields + self.fields = Arc::new(BlueprintStateFields { + ipv6: Some(ipv6), + ..(*self.fields).clone() + }); + + Ok(()) } pub fn set_ipv6( &mut self, + gsm_inner: &mut GlobalStateManagerInner, params: BlueprintStateIpv6Params, - gateway_params: Option, - ) { - let mut ipv6 = if let Some(ipv6) = self.fields.ipv6.clone() { + ) -> GlobalStateManagerResult<()> { + self.clear_ipv6(gsm_inner)?; + + let ipv6 = if let Some(ipv6) = self.fields.ipv6.clone() { BlueprintStateIpv6 { params, ..ipv6 } } else { BlueprintStateIpv6 { @@ -146,6 +256,26 @@ impl BlueprintState { } }; + // Update fields + self.fields = Arc::new(BlueprintStateFields { + ipv6: Some(ipv6), + ..(*self.fields).clone() + }); + + Ok(()) + } + + pub fn set_ipv6_gateway( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + gateway_params: Option, + ) -> GlobalStateManagerResult<()> { + self.clear_ipv6_gateway(gsm_inner)?; + + let Some(mut ipv6) = self.fields.ipv6.clone() else { + return Err(GlobalStateManagerError::InvalidGateway); + }; + if ipv6.gateway.is_some() { if let Some(gateway_params) = gateway_params { ipv6.gateway.as_mut().expect("must exist").params = gateway_params; @@ -163,6 +293,8 @@ impl BlueprintState { ipv6: Some(ipv6), ..(*self.fields).clone() }); + + Ok(()) } pub fn is_active(&self, gsm_inner: &mut GlobalStateManagerInner) -> bool { @@ -197,7 +329,7 @@ impl BlueprintState { }; let params = NetworkStateModelParams { - latency: Some(model.latency.clone()), + latency: model.latency.clone(), distance: model.distance.clone(), loss: model.loss, }; @@ -246,7 +378,11 @@ impl BlueprintState { }; // Get addresses for network - let Some(NetworkLocation { subnet, super_net }) = ipv4 + let Some(NetworkLocation { + scope, + reserve, + super_net, + }) = ipv4 .params .locations .pick_v4(gsm_inner, &ipv4.params.prefix)? @@ -257,7 +393,8 @@ impl BlueprintState { }; let params = NetworkStateIpv4Params { - allocation: subnet, + scope, + reserve, super_net, }; @@ -320,7 +457,10 @@ impl BlueprintState { None => None, }; - network_state.set_ipv4(gsm_inner, params, gateway_params)?; + network_state.set_ipv4(gsm_inner, params)?; + if let Some(gateway_params) = gateway_params { + network_state.set_ipv4_gateway(gsm_inner, gateway_params)?; + } Ok(()) } @@ -335,7 +475,11 @@ impl BlueprintState { }; // Get addresses for network - let Some(NetworkLocation { subnet, super_net }) = ipv6 + let Some(NetworkLocation { + scope, + reserve, + super_net, + }) = ipv6 .params .locations .pick_v6(gsm_inner, &ipv6.params.prefix)? @@ -346,7 +490,8 @@ impl BlueprintState { }; let params = NetworkStateIpv6Params { - allocation: subnet, + scope, + reserve, super_net, }; @@ -409,7 +554,10 @@ impl BlueprintState { None => None, }; - network_state.set_ipv6(gsm_inner, params, gateway_params)?; + network_state.set_ipv6(gsm_inner, params)?; + if let Some(gateway_params) = gateway_params { + network_state.set_ipv6_gateway(gsm_inner, gateway_params)?; + } Ok(()) } diff --git a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_locations_list.rs b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_locations_list.rs index 259d7632..5477d45f 100644 --- a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_locations_list.rs +++ b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_locations_list.rs @@ -13,7 +13,8 @@ pub enum NetworkLocationsList { #[derive(Debug, Clone)] pub struct NetworkLocation { - pub subnet: T, + pub scope: Vec, + pub reserve: Vec, pub super_net: Option, } @@ -35,37 +36,31 @@ impl NetworkLocationsList { NetworkLocationsList::Allocations { allocations } => { // Get allocations which have subnets that would fit // our maximum requested prefix - let Some(alloc_subnets) = allocations.try_filter_map(|allocation_name| { + let Some(address_pools) = allocations.try_filter_map(|allocation_name| { let allocation = gsm_inner .allocations() .get(allocation_name) .expect("must exist"); - Ok(allocation - .subnets - .subnet4 - .as_ref() - .and_then(|subnet| subnet.filter(|p| p.prefix_len() <= max_prefix))) + if allocation.address_pool.can_allocate_v4(max_prefix)? { + Ok(Some(allocation.address_pool.clone())) + } else { + Ok(None) + } })? else { return Ok(None); }; - // Pick an allocation - let subnets = gsm_inner.srng().weighted_choice_ref(&alloc_subnets); - - // Pick a subnet - let net = *gsm_inner.srng().weighted_choice_ref(subnets); + // Pick an address pool + let mut address_pool = gsm_inner.srng().weighted_choice(address_pools); // Pick a prefix length that would fit in the subnet let opt_subnet = prefix - .filter(|p| *p >= net.prefix_len()) + .try_filter(|p| address_pool.can_allocate_v4(*p))? .as_ref() .map(|wl| { let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(wl); - // Use an address pool temporarily to pick a subnet - let mut address_pool = AddressPool::<()>::new(); - address_pool.add_scope_v4(net); address_pool.allocate_random_v4(gsm_inner.srng(), subnet_prefix, ()) }) .transpose()? @@ -74,7 +69,8 @@ impl NetworkLocationsList { return Ok(None); }; Ok(Some(NetworkLocation { - subnet, + scope: vec![subnet], + reserve: Vec::new(), super_net: None, })) } @@ -126,7 +122,8 @@ impl NetworkLocationsList { .set_state(super_network_state); Ok(Some(NetworkLocation { - subnet, + scope: vec![subnet], + reserve: Vec::new(), super_net: Some(super_network_id), })) } @@ -150,37 +147,31 @@ impl NetworkLocationsList { NetworkLocationsList::Allocations { allocations } => { // Get allocations which have subnets that would fit // our maximum requested prefix - let Some(alloc_subnets) = allocations.try_filter_map(|allocation_name| { + let Some(address_pools) = allocations.try_filter_map(|allocation_name| { let allocation = gsm_inner .allocations() .get(allocation_name) .expect("must exist"); - Ok(allocation - .subnets - .subnet6 - .as_ref() - .and_then(|subnet| subnet.filter(|p| p.prefix_len() <= max_prefix))) + if allocation.address_pool.can_allocate_v6(max_prefix)? { + Ok(Some(allocation.address_pool.clone())) + } else { + Ok(None) + } })? else { return Ok(None); }; - // Pick an allocation - let subnets = gsm_inner.srng().weighted_choice_ref(&alloc_subnets); - - // Pick a subnet - let net = *gsm_inner.srng().weighted_choice_ref(subnets); + // Pick an address pool + let mut address_pool = gsm_inner.srng().weighted_choice(address_pools); // Pick a prefix length that would fit in the subnet let opt_subnet = prefix - .filter(|p| *p >= net.prefix_len()) + .try_filter(|p| address_pool.can_allocate_v6(*p))? .as_ref() .map(|wl| { let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(wl); - // Use an address pool temporarily to pick a subnet - let mut address_pool = AddressPool::<()>::new(); - address_pool.add_scope_v6(net); address_pool.allocate_random_v6(gsm_inner.srng(), subnet_prefix, ()) }) .transpose()? @@ -189,7 +180,8 @@ impl NetworkLocationsList { return Ok(None); }; Ok(Some(NetworkLocation { - subnet, + scope: vec![subnet], + reserve: Vec::new(), super_net: None, })) } @@ -241,7 +233,8 @@ impl NetworkLocationsList { .set_state(super_network_state); Ok(Some(NetworkLocation { - subnet, + scope: vec![subnet], + reserve: Vec::new(), super_net: Some(super_network_id), })) } diff --git a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_state.rs b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_state.rs index ce2c6aa6..c36e87df 100644 --- a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_state.rs +++ b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/network_state.rs @@ -44,7 +44,7 @@ struct NetworkStateModel { #[derive(Debug, Clone)] pub struct NetworkStateModelParams { /// Network latency distribution - pub latency: Option, + pub latency: config::Distribution, /// Distance simulation metric pub distance: Option, /// Packet loss probability @@ -53,7 +53,8 @@ pub struct NetworkStateModelParams { #[derive(Debug, Clone)] pub struct NetworkStateIpv4Params { - pub allocation: Ipv4Net, + pub scope: Vec, + pub reserve: Vec, pub super_net: Option, } @@ -65,7 +66,8 @@ struct NetworkStateIpv4 { #[derive(Debug, Clone)] pub struct NetworkStateIpv6Params { - pub allocation: Ipv6Net, + pub scope: Vec, + pub reserve: Vec, pub super_net: Option, } #[derive(Debug, Clone)] @@ -126,7 +128,7 @@ impl NetworkState { address_pool: AddressPool::new(), model: NetworkStateModel { params: NetworkStateModelParams { - latency: None, + latency: config::Distribution::default(), distance: None, loss: 0.0, }, @@ -144,9 +146,7 @@ impl NetworkState { .get_state(generating_blueprint) .expect("must exist"); blueprint_state.on_network_released(self.id()); - gsm_inner - .blueprint_states_mut() - .set_state(blueprint_state) + gsm_inner.blueprint_states_mut().set_state(blueprint_state) } } @@ -161,7 +161,9 @@ impl NetworkState { &mut self, gsm_inner: &mut GlobalStateManagerInner, ) -> GlobalStateManagerResult<()> { - let Some(ipv4) = self.fields.ipv4.clone() else { + self.clear_ipv4_gateway(gsm_inner)?; + + if self.fields.ipv4.is_none() { return Ok(()); }; @@ -176,28 +178,6 @@ impl NetworkState { GlobalStateManagerError::ResourceInUse(format!("{}-v4", self.debug_name())) })?; - // If we have a gateway, release its external address - // if it belongs to a different network - if let Some(gateway) = ipv4.gateway.as_ref() { - if gateway.params.external_network != self.id() { - // Get the external network state - let mut external_network_state = gsm_inner - .network_states() - .get_state(gateway.params.external_network) - .expect("must succeed"); - - // Release external address - external_network_state - .release_address_v4(gateway.external_interface_address.ip) - .expect("must succeed"); - - // Update external network - gsm_inner - .network_states_mut() - .set_state(external_network_state); - } - } - // Update fields self.fields = Arc::new(NetworkStateFields { ipv4: None, @@ -208,88 +188,168 @@ impl NetworkState { Ok(()) } + pub fn clear_ipv4_gateway( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + ) -> GlobalStateManagerResult<()> { + let Some(mut ipv4) = self.fields.ipv4.clone() else { + return Ok(()); + }; + let Some(gateway) = ipv4.gateway else { + return Ok(()); + }; + if gateway.params.external_network != self.id() { + // Get the external network state + let mut external_network_state = gsm_inner + .network_states() + .get_state(gateway.params.external_network) + .expect("must succeed"); + + // Release external address + external_network_state + .release_address_v4(gateway.external_interface_address.ip) + .expect("must succeed"); + + // Update external network + gsm_inner + .network_states_mut() + .set_state(external_network_state); + } + + // Release internal address + self.release_address_v4(gateway.internal_interface_address.ip) + .expect("must succeed"); + + // Clear gateway + ipv4.gateway = None; + + // Update fields + self.fields = Arc::new(NetworkStateFields { + ipv4: Some(ipv4), + ..(*self.fields).clone() + }); + + Ok(()) + } + pub fn set_ipv4( &mut self, gsm_inner: &mut GlobalStateManagerInner, params: NetworkStateIpv4Params, - gateway_params: Option, ) -> GlobalStateManagerResult<()> { self.clear_ipv4(gsm_inner)?; let mut address_pool = self.fields.address_pool.clone(); - address_pool.add_scope_v4(params.allocation); + for scope in ¶ms.scope { + address_pool.add_scope_v4(*scope); + } + for reserve in ¶ms.reserve { + address_pool.reserve_allocation_v4(*reserve, None)?; + } - let gateway = match gateway_params { - Some(gateway_params) => { - // Allocate or reserve an internal network address for the gateway - let internal_address = - if let Some(internal_address) = gateway_params.internal_address { - address_pool.reserve_allocation_v4( - Ipv4Net::new(internal_address, 32).expect("must succeed"), - Some(OwnerTag::Gateway(self.id())), - )?; - internal_address - } else { - let Some(internal_address) = address_pool.allocate_random_v4( - gsm_inner.srng(), - 32, - OwnerTag::Gateway(self.id()), - )? - else { - return Err(GlobalStateManagerError::NoAllocation); - }; - internal_address.addr() - }; - - // Make internal interface address - let internal_interface_address = Ifv4Addr { - ip: internal_address, - netmask: params.allocation.netmask(), - broadcast: Some(params.allocation.broadcast()), - }; - - // Get the external network state - let mut external_network_state = gsm_inner - .network_states() - .get_state(gateway_params.external_network) - .expect("must succeed"); - - // Allocate or reserve an external network address for the gateway - let external_interface_address = - if matches!(gateway_params.translation, config::Translation::None) { - // If the translation mode is 'none', then the external and internal - // addresses must be the same - external_network_state.allocate_address_v4( - gsm_inner, - OwnerTag::Gateway(self.id()), - Some(internal_address), - )? - } else { - // Network translation means the internal and external addresses - // will be different - external_network_state.allocate_address_v4( - gsm_inner, - OwnerTag::Gateway(self.id()), - None, - )? - }; - - // Update external network - gsm_inner - .network_states_mut() - .set_state(external_network_state); - - // Return the gateway state - Some(NetworkStateIpv4Gateway { - params: gateway_params, - internal_interface_address, - external_interface_address, - }) - } - None => None, + let ipv4 = NetworkStateIpv4 { + params, + gateway: None, }; - let ipv4 = NetworkStateIpv4 { params, gateway }; + // Update fields + self.fields = Arc::new(NetworkStateFields { + ipv4: Some(ipv4), + address_pool, + ..(*self.fields).clone() + }); + + Ok(()) + } + + pub fn set_ipv4_gateway( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + gateway_params: NetworkStateIpv4GatewayParams, + ) -> GlobalStateManagerResult<()> { + self.clear_ipv4_gateway(gsm_inner)?; + + let Some(mut ipv4) = self.fields.ipv4.clone() else { + return Err(GlobalStateManagerError::InvalidGateway); + }; + + let mut address_pool = self.fields.address_pool.clone(); + + // Allocate or reserve an internal network address for the gateway + let internal_interface_address = + if let Some(internal_address) = gateway_params.internal_address { + let scope = address_pool.reserve_allocation_v4( + Ipv4Net::new(internal_address, 32).expect("must succeed"), + Some(OwnerTag::Gateway(self.id())), + )?; + + // Make interface address + Ifv4Addr { + ip: internal_address, + netmask: scope.netmask(), + broadcast: Some(scope.broadcast()), + } + } else { + let Some(internal_address) = address_pool.allocate_random_v4( + gsm_inner.srng(), + 32, + OwnerTag::Gateway(self.id()), + )? + else { + return Err(GlobalStateManagerError::NoAllocation); + }; + + // Get the scope this allocation fits in + let scope = address_pool + .find_scope_v4(internal_address) + .expect("must succeed"); + + // Make interface address + let internal_address = internal_address.addr(); + Ifv4Addr { + ip: internal_address, + netmask: scope.netmask(), + broadcast: Some(scope.broadcast()), + } + }; + + // Get the external network state + let mut external_network_state = gsm_inner + .network_states() + .get_state(gateway_params.external_network) + .expect("must succeed"); + + // Allocate or reserve an external network address for the gateway + let external_interface_address = + if matches!(gateway_params.translation, config::Translation::None) { + // If the translation mode is 'none', then the external and internal + // addresses must be the same + external_network_state.allocate_address_v4( + gsm_inner, + OwnerTag::Gateway(self.id()), + Some(internal_interface_address.ip), + )? + } else { + // Network translation means the internal and external addresses + // will be different + external_network_state.allocate_address_v4( + gsm_inner, + OwnerTag::Gateway(self.id()), + None, + )? + }; + + // Update external network + gsm_inner + .network_states_mut() + .set_state(external_network_state); + + // Set the gateway state + ipv4.gateway = Some(NetworkStateIpv4Gateway { + params: gateway_params, + internal_interface_address, + external_interface_address, + }); // Update fields self.fields = Arc::new(NetworkStateFields { @@ -305,7 +365,9 @@ impl NetworkState { &mut self, gsm_inner: &mut GlobalStateManagerInner, ) -> GlobalStateManagerResult<()> { - let Some(ipv6) = self.fields.ipv6.clone() else { + self.clear_ipv6_gateway(gsm_inner)?; + + if self.fields.ipv6.is_none() { return Ok(()); }; @@ -320,28 +382,6 @@ impl NetworkState { GlobalStateManagerError::ResourceInUse(format!("{}-v6", self.debug_name())) })?; - // If we have a gateway, release its external address - // if it belongs to a different network - if let Some(gateway) = ipv6.gateway.as_ref() { - if gateway.params.external_network != self.id() { - // Get the external network state - let mut external_network_state = gsm_inner - .network_states() - .get_state(gateway.params.external_network) - .expect("must succeed"); - - // Release external address - external_network_state - .release_address_v6(gateway.external_interface_address.ip) - .expect("must succeed"); - - // Update external network - gsm_inner - .network_states_mut() - .set_state(external_network_state); - } - } - // Update fields self.fields = Arc::new(NetworkStateFields { ipv6: None, @@ -351,88 +391,166 @@ impl NetworkState { Ok(()) } + + pub fn clear_ipv6_gateway( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + ) -> GlobalStateManagerResult<()> { + let Some(mut ipv6) = self.fields.ipv6.clone() else { + return Ok(()); + }; + let Some(gateway) = ipv6.gateway else { + return Ok(()); + }; + if gateway.params.external_network != self.id() { + // Get the external network state + let mut external_network_state = gsm_inner + .network_states() + .get_state(gateway.params.external_network) + .expect("must succeed"); + + // Release external address + external_network_state + .release_address_v6(gateway.external_interface_address.ip) + .expect("must succeed"); + + // Update external network + gsm_inner + .network_states_mut() + .set_state(external_network_state); + } + + // Release internal address + self.release_address_v6(gateway.internal_interface_address.ip) + .expect("must succeed"); + + // Clear gateway + ipv6.gateway = None; + + // Update fields + self.fields = Arc::new(NetworkStateFields { + ipv6: Some(ipv6), + ..(*self.fields).clone() + }); + + Ok(()) + } + pub fn set_ipv6( &mut self, gsm_inner: &mut GlobalStateManagerInner, params: NetworkStateIpv6Params, - gateway_params: Option, ) -> GlobalStateManagerResult<()> { self.clear_ipv6(gsm_inner)?; let mut address_pool = self.fields.address_pool.clone(); - address_pool.add_scope_v6(params.allocation); - - let gateway = match gateway_params { - Some(gateway_params) => { - // Allocate or reserve an internal network address for the gateway - let internal_address = - if let Some(internal_address) = gateway_params.internal_address { - address_pool.reserve_allocation_v6( - Ipv6Net::new(internal_address, 128).expect("must succeed"), - Some(OwnerTag::Gateway(self.id())), - )?; - internal_address - } else { - let Some(internal_address) = address_pool.allocate_random_v6( - gsm_inner.srng(), - 128, - OwnerTag::Gateway(self.id()), - )? - else { - return Err(GlobalStateManagerError::NoAllocation); - }; - internal_address.addr() - }; - - // Make internal interface address - let internal_interface_address = Ifv6Addr { - ip: internal_address, - netmask: params.allocation.netmask(), - broadcast: Some(params.allocation.broadcast()), - }; - - // Get the external network state - let mut external_network_state = gsm_inner - .network_states() - .get_state(gateway_params.external_network) - .expect("must succeed"); - - // Allocate or reserve an external network address for the gateway - let external_interface_address = - if matches!(gateway_params.translation, config::Translation::None) { - // If the translation mode is 'none', then the external and internal - // addresses must be the same - external_network_state.allocate_address_v6( - gsm_inner, - OwnerTag::Gateway(self.id()), - Some(internal_address), - )? - } else { - // Network translation means the internal and external addresses - // will be different - external_network_state.allocate_address_v6( - gsm_inner, - OwnerTag::Gateway(self.id()), - None, - )? - }; - - // Update external network - gsm_inner - .network_states_mut() - .set_state(external_network_state); - - // Return the gateway state - Some(NetworkStateIpv6Gateway { - params: gateway_params, - internal_interface_address, - external_interface_address, - }) - } - None => None, + for scope in ¶ms.scope { + address_pool.add_scope_v6(*scope); + } + for reserve in ¶ms.reserve { + address_pool.reserve_allocation_v6(*reserve, None)?; + } + let ipv6 = NetworkStateIpv6 { + params, + gateway: None, }; - let ipv6 = NetworkStateIpv6 { params, gateway }; + // Update fields + self.fields = Arc::new(NetworkStateFields { + ipv6: Some(ipv6), + address_pool, + ..(*self.fields).clone() + }); + + Ok(()) + } + + pub fn set_ipv6_gateway( + &mut self, + gsm_inner: &mut GlobalStateManagerInner, + gateway_params: NetworkStateIpv6GatewayParams, + ) -> GlobalStateManagerResult<()> { + self.clear_ipv6_gateway(gsm_inner)?; + + let Some(mut ipv6) = self.fields.ipv6.clone() else { + return Err(GlobalStateManagerError::InvalidGateway); + }; + + let mut address_pool = self.fields.address_pool.clone(); + + // Allocate or reserve an internal network address for the gateway + let internal_interface_address = + if let Some(internal_address) = gateway_params.internal_address { + let scope = address_pool.reserve_allocation_v6( + Ipv6Net::new(internal_address, 128).expect("must succeed"), + Some(OwnerTag::Gateway(self.id())), + )?; + // Make interface address + Ifv6Addr { + ip: internal_address, + netmask: scope.netmask(), + broadcast: Some(scope.broadcast()), + } + } else { + let Some(internal_address) = address_pool.allocate_random_v6( + gsm_inner.srng(), + 128, + OwnerTag::Gateway(self.id()), + )? + else { + return Err(GlobalStateManagerError::NoAllocation); + }; + // Get the scope this allocation fits in + let scope = address_pool + .find_scope_v6(internal_address) + .expect("must succeed"); + + // Make interface address + let internal_address = internal_address.addr(); + Ifv6Addr { + ip: internal_address, + netmask: scope.netmask(), + broadcast: Some(scope.broadcast()), + } + }; + + // Get the external network state + let mut external_network_state = gsm_inner + .network_states() + .get_state(gateway_params.external_network) + .expect("must succeed"); + + // Allocate or reserve an external network address for the gateway + let external_interface_address = + if matches!(gateway_params.translation, config::Translation::None) { + // If the translation mode is 'none', then the external and internal + // addresses must be the same + external_network_state.allocate_address_v6( + gsm_inner, + OwnerTag::Gateway(self.id()), + Some(internal_interface_address.ip), + )? + } else { + // Network translation means the internal and external addresses + // will be different + external_network_state.allocate_address_v6( + gsm_inner, + OwnerTag::Gateway(self.id()), + None, + )? + }; + + // Update external network + gsm_inner + .network_states_mut() + .set_state(external_network_state); + + // Set the gateway state + ipv6.gateway = Some(NetworkStateIpv6Gateway { + params: gateway_params, + internal_interface_address, + external_interface_address, + }); // Update fields self.fields = Arc::new(NetworkStateFields { @@ -477,11 +595,14 @@ impl NetworkState { opt_address: Option, ) -> GlobalStateManagerResult { let net = self.allocate_subnet_v4(gsm_inner, owner_tag, opt_address, 32)?; + let scope = self + .fields + .address_pool + .find_scope_v4(net) + .expect("must succeed"); let ip = net.addr(); - - let ipv4 = self.fields.ipv4.as_ref().unwrap(); - let netmask = ipv4.params.allocation.netmask(); - let broadcast = ipv4.params.allocation.broadcast(); + let netmask = scope.netmask(); + let broadcast = scope.broadcast(); let ifaddr = Ifv4Addr { ip, @@ -517,11 +638,8 @@ impl NetworkState { net } else { // Get a random address if available - let Some(allocation) = address_pool.allocate_random_v4( - gsm_inner.srng(), - prefix, - owner_tag, - )? + let Some(allocation) = + address_pool.allocate_random_v4(gsm_inner.srng(), prefix, owner_tag)? else { return Err(GlobalStateManagerError::NoAllocation); }; @@ -585,11 +703,15 @@ impl NetworkState { opt_address: Option, ) -> GlobalStateManagerResult { let net = self.allocate_subnet_v6(gsm_inner, owner_tag, opt_address, 128)?; - let ip = net.addr(); + let scope = self + .fields + .address_pool + .find_scope_v6(net) + .expect("must succeed"); - let ipv6 = self.fields.ipv6.as_ref().unwrap(); - let netmask = ipv6.params.allocation.netmask(); - let broadcast = ipv6.params.allocation.broadcast(); + let ip = net.addr(); + let netmask = scope.netmask(); + let broadcast = scope.broadcast(); let ifaddr = Ifv6Addr { ip, @@ -625,11 +747,8 @@ impl NetworkState { net } else { // Get a random address if available - let Some(allocation) = address_pool.allocate_random_v6( - gsm_inner.srng(), - prefix, - owner_tag, - )? + let Some(allocation) = + address_pool.allocate_random_v6(gsm_inner.srng(), prefix, owner_tag)? else { return Err(GlobalStateManagerError::NoAllocation); }; diff --git a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/state_registry.rs b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/state_registry.rs index 629d9c84..d1fc23e0 100644 --- a/veilid-tools/src/virtual_network/router_server/global_state_manager/state/state_registry.rs +++ b/veilid-tools/src/virtual_network/router_server/global_state_manager/state/state_registry.rs @@ -65,20 +65,6 @@ impl StateRegistry { } } - pub fn get_or_create_by_name, String) -> S>( - &mut self, - name: String, - create: F, - ) -> StateId { - if let Some(id) = self.get_state_id_by_name(&name) { - return id; - } - let id = self.allocate_id(); - let state = create(id, name); - self.attach_state(state).expect("should always attach"); - id - } - pub fn allocate_id(&mut self) -> StateId { // Allocate new internal id let state_id = self.free_state_ids.pop_back().unwrap_or_else(|| { diff --git a/veilid-tools/src/virtual_network/router_server/predefined_config.yml b/veilid-tools/src/virtual_network/router_server/predefined_config.yml index 4230ae03..543df918 100644 --- a/veilid-tools/src/virtual_network/router_server/predefined_config.yml +++ b/veilid-tools/src/virtual_network/router_server/predefined_config.yml @@ -14,6 +14,9 @@ default_network: "$internet" # this is '$lan') default_model: "$lan" +# The name of the default allocation pool that subnets are allocated from +default_pool: "$internet" + ################################################################# # Networks # @@ -38,54 +41,56 @@ networks: # will be used (on the 'public internet'). allocations: - # Predefined networks - $internet: {} + # Predefined allocations + $internet: + scope4: ["0.0.0.0/0"] + scope6: ["::/0"] $private: - subnet4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] - subnet6: ["fc00::/7"] + scope4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] + scope6: ["fc00::/7"] $cgnat: - subnet4: ["100.64.0.0/10"] + scope4: ["100.64.0.0/10"] $linklocal: - subnet4: ["169.254.0.0/16"] - subnet6: ["fe80::/10"] + scope4: ["169.254.0.0/16"] + scope6: ["fe80::/10"] $localhost: - subnet4: ["127.0.0.0/8"] - subnet6: ["::1/128"] + scope4: ["127.0.0.0/8"] + scope6: ["::1/128"] $ietf: - subnet4: ["192.0.0.0/24"] + scope4: ["192.0.0.0/24"] $cellnat: - subnet4: ["192.0.0.0/29"] + scope4: ["192.0.0.0/29"] $documentation: - subnet4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"] - subnet6: ["2001:db8::/32", "3fff::/20"] + scope4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"] + scope6: ["2001:db8::/32", "3fff::/20"] $benchmark: - subnet4: ["198.18.0.0/15"] + scope4: ["198.18.0.0/15"] $mulitcast: - subnet4: ["224.0.0.0/4"] + scope4: ["224.0.0.0/4"] $mulitcasttest: - subnet4: ["233.252.0.0/24"] - subnet6: ["ff00::/8"] + scope4: ["233.252.0.0/24"] + scope6: ["ff00::/8"] $unspecified: - subnet4: ["0.0.0.0/8"] - subnet6: ["::/128"] + scope4: ["0.0.0.0/8"] + scope6: ["::/128"] $reserved: - subnet4: ["192.88.99.0/24", "240.0.0.0/4"] + scope4: ["192.88.99.0/24", "240.0.0.0/4"] $broadcast: - subnet4: ["255.255.255.255/32"] + scope4: ["255.255.255.255/32"] $mapped: - subnet6: ["::ffff:0:0/96", "::ffff:0:0:0/96"] + scope6: ["::ffff:0:0/96", "::ffff:0:0:0/96"] $translation: - subnet6: ["64:ff9b::/96", "64:ff9b:1::/48"] + scope6: ["64:ff9b::/96", "64:ff9b:1::/48"] $discard: - subnet6: ["100::/64"] + scope6: ["100::/64"] $teredo: - subnet6: ["2001::/32"] + scope6: ["2001::/32"] $orchidv2: - subnet6: ["2001:20::/28"] + scope6: ["2001:20::/28"] $6to4: - subnet6: ["2002::/16"] + scope6: ["2002::/16"] $srv6: - subnet6: ["5f00::/16"] + scope6: ["5f00::/16"] ################################################################# # Models diff --git a/veilid-tools/src/virtual_network/router_server/weighted_list.rs b/veilid-tools/src/virtual_network/router_server/weighted_list.rs index 80666ff1..6e1a9dee 100644 --- a/veilid-tools/src/virtual_network/router_server/weighted_list.rs +++ b/veilid-tools/src/virtual_network/router_server/weighted_list.rs @@ -156,6 +156,34 @@ impl WeightedList { } } + pub fn try_map(&self, mut filter: F) -> Result, E> + where + F: FnMut(&T) -> Result, + S: fmt::Debug + Clone, + { + match self { + WeightedList::Single(v) => { + let item = filter(v)?; + Ok(WeightedList::Single(item)) + } + WeightedList::List(vec) => { + let mut out = Vec::>::with_capacity(vec.len()); + for v in vec { + let item = filter(v.item())?; + + out.push(match v { + Weighted::Weighted { item: _, weight } => Weighted::Weighted { + item, + weight: *weight, + }, + Weighted::Unweighted(_) => Weighted::Unweighted(item), + }); + } + Ok(WeightedList::List(out)) + } + } + } + pub fn iter(&self) -> WeightedListIter<'_, T> { WeightedListIter { values: self,