[skip ci] blueprint config execution

This commit is contained in:
Christien Rioux 2025-01-14 20:23:09 -05:00
parent 80c85f0e35
commit a7eee5574b
12 changed files with 1343 additions and 800 deletions

View File

@ -80,9 +80,9 @@ fn main() -> Result<(), String> {
let router_server = virtual_network::RouterServer::new(); let router_server = virtual_network::RouterServer::new();
if let Err(e) = router_server.execute_config(initial_config) { router_server
xxx continue here .execute_config(initial_config)
} .map_err(|e| format!("Error executing config: {}", e))?;
let _ss_tcp = if !args.no_tcp { let _ss_tcp = if !args.no_tcp {
Some( Some(

View File

@ -120,10 +120,10 @@ pub struct TemplateLimits {
/// maximum number of machines this template will generate /// maximum number of machines this template will generate
#[validate(nested)] #[validate(nested)]
#[serde(default)] #[serde(default)]
pub machine_count: Option<WeightedList<u32>>, pub machine_count: Option<WeightedList<usize>>,
#[validate(nested)] #[validate(nested)]
#[serde(default)] #[serde(default)]
pub machines_per_network: Option<WeightedList<u32>>, pub machines_per_network: Option<WeightedList<usize>>,
} }
fn validate_template_limits(limits: &TemplateLimits) -> Result<(), ValidationError> { fn validate_template_limits(limits: &TemplateLimits) -> Result<(), ValidationError> {
@ -255,7 +255,7 @@ pub struct BlueprintLimits {
/// maximum number of networks this blueprint will generate /// maximum number of networks this blueprint will generate
#[validate(nested)] #[validate(nested)]
#[serde(default)] #[serde(default)]
pub network_count: Option<WeightedList<u32>>, pub network_count: Option<WeightedList<usize>>,
} }
fn validate_blueprint_limits(limits: &BlueprintLimits) -> Result<(), ValidationError> { fn validate_blueprint_limits(limits: &BlueprintLimits) -> Result<(), ValidationError> {
@ -329,7 +329,7 @@ fn validate_blueprint_ipv4(blueprint_ipv4: &BlueprintIpv4) -> Result<(), Validat
pub struct BlueprintIpv6 { pub struct BlueprintIpv6 {
#[serde(flatten)] #[serde(flatten)]
#[validate(nested)] #[validate(nested)]
pub allocation: BlueprintLocation, pub location: BlueprintLocation,
#[validate(nested)] #[validate(nested)]
pub prefix: WeightedList<u8>, pub prefix: WeightedList<u8>,
#[serde(default)] #[serde(default)]
@ -354,29 +354,27 @@ pub struct BlueprintGateway {
pub translation: WeightedList<Translation>, pub translation: WeightedList<Translation>,
#[validate(range(min = 0.0, max = 1.0))] #[validate(range(min = 0.0, max = 1.0))]
pub upnp: Probability, pub upnp: Probability,
#[serde(flatten)] #[serde(default, flatten)]
pub location: TemplateLocation, #[validate(nested)]
pub location: Option<TemplateLocation>,
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, Serialize, Deserialize, Validate)] #[derive(Debug, Clone, Serialize, Deserialize, Validate)]
#[validate(schema(function = "validate_subnets"))] pub struct Scope4 {
pub struct Subnets { #[validate(length(min = 1))]
pub scope4: Vec<Ipv4Net>,
#[serde(default)] #[serde(default)]
#[validate(nested)] pub pool4: Option<String>,
pub subnet4: Option<WeightedList<Ipv4Net>>,
#[serde(default)]
#[validate(nested)]
pub subnet6: Option<WeightedList<Ipv6Net>>,
} }
fn validate_subnets(subnets: &Subnets) -> Result<(), ValidationError> { #[derive(Debug, Clone, Serialize, Deserialize, Validate)]
if subnets.subnet4.is_none() && subnets.subnet6.is_none() { pub struct Scope6 {
return Err(ValidationError::new("badsub") #[validate(length(min = 1))]
.with_message("subnets must support at least one address type".into())); pub scope6: Vec<Ipv6Net>,
} #[serde(default)]
Ok(()) pub pool6: Option<String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Validate)] #[derive(Debug, Clone, Serialize, Deserialize, Validate)]
@ -398,7 +396,7 @@ fn validate_distance(distance: &Distance) -> Result<(), ValidationError> {
Ok(()) Ok(())
} }
#[derive(Debug, Clone, Serialize, Deserialize, Validate)] #[derive(Debug, Clone, Serialize, Deserialize, Validate, Default)]
#[validate(schema(function = "validate_distribution"))] #[validate(schema(function = "validate_distribution"))]
pub struct Distribution { pub struct Distribution {
pub mean: f32, pub mean: f32,
@ -455,7 +453,10 @@ pub struct Model {
pub struct Allocation { pub struct Allocation {
#[serde(flatten)] #[serde(flatten)]
#[validate(nested)] #[validate(nested)]
pub subnets: Subnets, pub scope4: Option<Scope4>,
#[serde(flatten)]
#[validate(nested)]
pub scope6: Option<Scope6>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[derive(Debug, Clone, Serialize, Deserialize, Default)]
@ -467,6 +468,8 @@ pub struct Config {
#[serde(default)] #[serde(default)]
pub default_model: Option<String>, pub default_model: Option<String>,
#[serde(default)] #[serde(default)]
pub default_pool: Option<String>,
#[serde(default)]
pub profiles: HashMap<String, Profile>, pub profiles: HashMap<String, Profile>,
#[serde(default)] #[serde(default)]
pub machines: HashMap<String, Machine>, pub machines: HashMap<String, Machine>,
@ -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("profiles", validate_hash_map(&self.profiles));
errors.merge_self("machines", validate_hash_map(&self.machines)); errors.merge_self("machines", validate_hash_map(&self.machines));
errors.merge_self("templates", validate_hash_map(&self.templates)); errors.merge_self("templates", validate_hash_map(&self.templates));
@ -575,6 +589,7 @@ impl Config {
seed: other.seed.or(self.seed), seed: other.seed.or(self.seed),
default_network: other.default_network.or(self.default_network), default_network: other.default_network.or(self.default_network),
default_model: other.default_model.or(self.default_model), 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(), profiles: self.profiles.into_iter().chain(other.profiles).collect(),
machines: self.machines.into_iter().chain(other.machines).collect(), machines: self.machines.into_iter().chain(other.machines).collect(),
templates: self.templates.into_iter().chain(other.templates).collect(), templates: self.templates.into_iter().chain(other.templates).collect(),

View File

@ -14,6 +14,9 @@
# this is '$lan') # this is '$lan')
# default_model: "$lan" # default_model: "$lan"
# The name of the default allocation pool that subnets are allocated from
# default_pool: "$internet"
################################################################# #################################################################
# Profiles # Profiles
# #
@ -174,56 +177,58 @@ blueprints:
allocations: allocations:
# Custom network allocations # Custom network allocations
boot: boot:
subnet4: ["170.64.128.0/24"] scope4: ["170.64.128.0/24"]
subnet6: ["2a03:b0c0:2::/48"] scope6: ["2a03:b0c0:2::/48"]
# # Predefined networks # # Predefined allocations
# $internet: {} # $internet:
# scope4: ["0.0.0.0/0"]
# scope6: ["::/0"]
# $private: # $private:
# subnet4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] # scope4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
# subnet6: ["fc00::/7"] # scope6: ["fc00::/7"]
# $cgnat: # $cgnat:
# subnet4: ["100.64.0.0/10"] # scope4: ["100.64.0.0/10"]
# $linklocal: # $linklocal:
# subnet4: ["169.254.0.0/16"] # scope4: ["169.254.0.0/16"]
# subnet6: ["fe80::/10"] # scope6: ["fe80::/10"]
# $localhost: # $localhost:
# subnet4: ["127.0.0.0/8"] # scope4: ["127.0.0.0/8"]
# subnet6: ["::1/128"] # scope6: ["::1/128"]
# $ietf: # $ietf:
# subnet4: ["192.0.0.0/24"] # scope4: ["192.0.0.0/24"]
# $cellnat: # $cellnat:
# subnet4: ["192.0.0.0/29"] # scope4: ["192.0.0.0/29"]
# $documentation: # $documentation:
# subnet4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"] # scope4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
# subnet6: ["2001:db8::/32", "3fff::/20"] # scope6: ["2001:db8::/32", "3fff::/20"]
# $benchmark: # $benchmark:
# subnet4: ["198.18.0.0/15"] # scope4: ["198.18.0.0/15"]
# $mulitcast: # $mulitcast:
# subnet4: ["224.0.0.0/4"] # scope4: ["224.0.0.0/4"]
# $mulitcasttest: # $mulitcasttest:
# subnet4: ["233.252.0.0/24"] # scope4: ["233.252.0.0/24"]
# subnet6: ["ff00::/8"] # scope6: ["ff00::/8"]
# $unspecified: # $unspecified:
# subnet4: ["0.0.0.0/8"] # scope4: ["0.0.0.0/8"]
# subnet6: ["::/128"] # scope6: ["::/128"]
# $reserved: # $reserved:
# subnet4: ["192.88.99.0/24", "240.0.0.0/4"] # scope4: ["192.88.99.0/24", "240.0.0.0/4"]
# $broadcast: # $broadcast:
# subnet4: ["255.255.255.255/32"] # scope4: ["255.255.255.255/32"]
# $mapped: # $mapped:
# subnet6: ["::ffff:0:0/96", "::ffff:0:0:0/96"] # scope6: ["::ffff:0:0/96", "::ffff:0:0:0/96"]
# $translation: # $translation:
# subnet6: ["64:ff9b::/96", "64:ff9b:1::/48"] # scope6: ["64:ff9b::/96", "64:ff9b:1::/48"]
# $discard: # $discard:
# subnet6: ["100::/64"] # scope6: ["100::/64"]
# $teredo: # $teredo:
# subnet6: ["2001::/32"] # scope6: ["2001::/32"]
# $orchidv2: # $orchidv2:
# subnet6: ["2001:20::/28"] # scope6: ["2001:20::/28"]
# $6to4: # $6to4:
# subnet6: ["2002::/16"] # scope6: ["2002::/16"]
# $srv6: # $srv6:
# subnet6: ["5f00::/16"] # scope6: ["5f00::/16"]
################################################################# #################################################################
# Models # Models
# #

View File

@ -26,6 +26,19 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
pub fn scopes_v4(&self) -> Vec<Ipv4Net> {
self.scope_v4.iter().cloned().collect()
}
pub fn allocations_v4(&self) -> Vec<Ipv4Net> {
self.allocated_v4.iter().cloned().collect()
}
pub fn scopes_v6(&self) -> Vec<Ipv6Net> {
self.scope_v6.iter().cloned().collect()
}
pub fn allocations_v6(&self) -> Vec<Ipv6Net> {
self.allocated_v6.iter().cloned().collect()
}
pub fn add_scope_v4(&mut self, allocation: Ipv4Net) { pub fn add_scope_v4(&mut self, allocation: Ipv4Net) {
let mut scopes = self.scope_v4.iter().copied().collect::<Vec<_>>(); let mut scopes = self.scope_v4.iter().copied().collect::<Vec<_>>();
scopes.push(allocation); scopes.push(allocation);
@ -40,22 +53,22 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
self.scope_v6 = scopes.into(); self.scope_v6 = scopes.into();
} }
pub fn is_in_scope_v4(&self, allocation: Ipv4Net) -> bool { pub fn find_scope_v4(&self, allocation: Ipv4Net) -> Option<Ipv4Net> {
for x in &self.scope_v4 { for x in &self.scope_v4 {
if x.contains(&allocation) { 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<Ipv6Net> {
for x in &self.scope_v6 { for x in &self.scope_v6 {
if x.contains(&allocation) { if x.contains(&allocation) {
return true; return Some(*x);
} }
} }
false None
} }
pub fn can_allocate_v6(&self, prefix: u8) -> GlobalStateManagerResult<bool> { pub fn can_allocate_v6(&self, prefix: u8) -> GlobalStateManagerResult<bool> {
@ -82,11 +95,11 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
&mut self, &mut self,
allocation: Ipv4Net, allocation: Ipv4Net,
opt_tag: Option<T>, opt_tag: Option<T>,
) -> GlobalStateManagerResult<()> { ) -> GlobalStateManagerResult<Ipv4Net> {
// Ensure the allocation is in our scope // 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); return Err(GlobalStateManagerError::NoAllocation);
} };
// Only reserve if it's not overlapping an allocation // Only reserve if it's not overlapping an allocation
if !self.get_overlaps_v4(allocation).is_empty() { if !self.get_overlaps_v4(allocation).is_empty() {
@ -97,18 +110,18 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
self.allocated_v4.insert_ord(allocation); self.allocated_v4.insert_ord(allocation);
self.owner_tags_v4.insert(allocation, opt_tag); self.owner_tags_v4.insert(allocation, opt_tag);
Ok(()) Ok(scope)
} }
pub fn reserve_allocation_v6( pub fn reserve_allocation_v6(
&mut self, &mut self,
allocation: Ipv6Net, allocation: Ipv6Net,
opt_tag: Option<T>, opt_tag: Option<T>,
) -> GlobalStateManagerResult<()> { ) -> GlobalStateManagerResult<Ipv6Net> {
// Ensure the allocation is in our scope // 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); return Err(GlobalStateManagerError::NoAllocation);
} };
// Only reserve if it's not overlapping an allocation // Only reserve if it's not overlapping an allocation
if !self.get_overlaps_v6(allocation).is_empty() { if !self.get_overlaps_v6(allocation).is_empty() {
@ -119,7 +132,7 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
self.allocated_v6.insert_ord(allocation); self.allocated_v6.insert_ord(allocation);
self.owner_tags_v6.insert(allocation, opt_tag); self.owner_tags_v6.insert(allocation, opt_tag);
Ok(()) Ok(scope)
} }
pub fn get_overlaps_v4(&self, allocation: Ipv4Net) -> Vec<Ipv4Net> { pub fn get_overlaps_v4(&self, allocation: Ipv4Net) -> Vec<Ipv4Net> {

View File

@ -48,14 +48,20 @@ pub enum GlobalStateManagerError {
BlueprintNotFound(String), BlueprintNotFound(String),
#[error("Model not found: {0}")] #[error("Model not found: {0}")]
ModelNotFound(String), ModelNotFound(String),
#[error("Allocation not found: {0}")]
AllocationNotFound(String),
#[error("No default model")] #[error("No default model")]
NoDefaultModel, NoDefaultModel,
#[error("No default network")] #[error("No default network")]
NoDefaultNetwork, NoDefaultNetwork,
#[error("No default pool")]
NoDefaultPool,
#[error("No allocation available")] #[error("No allocation available")]
NoAllocation, NoAllocation,
#[error("Resource in use: {0}")] #[error("Resource in use: {0}")]
ResourceInUse(String), ResourceInUse(String),
#[error("Invalid gateway")]
InvalidGateway,
} }
pub type GlobalStateManagerResult<T> = Result<T, GlobalStateManagerError>; pub type GlobalStateManagerResult<T> = Result<T, GlobalStateManagerError>;

View File

@ -69,8 +69,8 @@ pub struct BlueprintState {
pub type BlueprintStateId = StateId<BlueprintState>; pub type BlueprintStateId = StateId<BlueprintState>;
impl BlueprintState { impl BlueprintState {
pub fn new(id: BlueprintStateId, name: String) -> GlobalStateManagerResult<BlueprintState> { pub fn new(id: BlueprintStateId, name: String) -> Self {
Ok(Self { Self {
immutable: Arc::new(BlueprintStateImmutable { id, name }), immutable: Arc::new(BlueprintStateImmutable { id, name }),
fields: Arc::new(BlueprintStateFields { fields: Arc::new(BlueprintStateFields {
limit_network_count: None, limit_network_count: None,
@ -79,7 +79,7 @@ impl BlueprintState {
ipv4: None, ipv4: None,
ipv6: None, ipv6: None,
}), }),
}) }
} }
pub fn set_limit_network_count(&mut self, limit_network_count: Option<usize>) { pub fn set_limit_network_count(&mut self, limit_network_count: Option<usize>) {
@ -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( pub fn set_ipv4(
&mut self, &mut self,
gsm_inner: &mut GlobalStateManagerInner,
params: BlueprintStateIpv4Params, params: BlueprintStateIpv4Params,
gateway_params: Option<BlueprintStateGatewayParams>, ) -> GlobalStateManagerResult<()> {
) { self.clear_ipv4(gsm_inner)?;
let mut ipv4 = if let Some(ipv4) = self.fields.ipv4.clone() {
let ipv4 = if let Some(ipv4) = self.fields.ipv4.clone() {
BlueprintStateIpv4 { params, ..ipv4 } BlueprintStateIpv4 { params, ..ipv4 }
} else { } else {
BlueprintStateIpv4 { 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<BlueprintStateGatewayParams>,
) -> 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 ipv4.gateway.is_some() {
if let Some(gateway_params) = gateway_params { if let Some(gateway_params) = gateway_params {
ipv4.gateway.as_mut().expect("must exist").params = gateway_params; ipv4.gateway.as_mut().expect("must exist").params = gateway_params;
@ -130,14 +194,60 @@ impl BlueprintState {
ipv4: Some(ipv4), ipv4: Some(ipv4),
..(*self.fields).clone() ..(*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( pub fn set_ipv6(
&mut self, &mut self,
gsm_inner: &mut GlobalStateManagerInner,
params: BlueprintStateIpv6Params, params: BlueprintStateIpv6Params,
gateway_params: Option<BlueprintStateGatewayParams>, ) -> GlobalStateManagerResult<()> {
) { self.clear_ipv6(gsm_inner)?;
let mut ipv6 = if let Some(ipv6) = self.fields.ipv6.clone() {
let ipv6 = if let Some(ipv6) = self.fields.ipv6.clone() {
BlueprintStateIpv6 { params, ..ipv6 } BlueprintStateIpv6 { params, ..ipv6 }
} else { } else {
BlueprintStateIpv6 { 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<BlueprintStateGatewayParams>,
) -> 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 ipv6.gateway.is_some() {
if let Some(gateway_params) = gateway_params { if let Some(gateway_params) = gateway_params {
ipv6.gateway.as_mut().expect("must exist").params = gateway_params; ipv6.gateway.as_mut().expect("must exist").params = gateway_params;
@ -163,6 +293,8 @@ impl BlueprintState {
ipv6: Some(ipv6), ipv6: Some(ipv6),
..(*self.fields).clone() ..(*self.fields).clone()
}); });
Ok(())
} }
pub fn is_active(&self, gsm_inner: &mut GlobalStateManagerInner) -> bool { pub fn is_active(&self, gsm_inner: &mut GlobalStateManagerInner) -> bool {
@ -197,7 +329,7 @@ impl BlueprintState {
}; };
let params = NetworkStateModelParams { let params = NetworkStateModelParams {
latency: Some(model.latency.clone()), latency: model.latency.clone(),
distance: model.distance.clone(), distance: model.distance.clone(),
loss: model.loss, loss: model.loss,
}; };
@ -246,7 +378,11 @@ impl BlueprintState {
}; };
// Get addresses for network // Get addresses for network
let Some(NetworkLocation { subnet, super_net }) = ipv4 let Some(NetworkLocation {
scope,
reserve,
super_net,
}) = ipv4
.params .params
.locations .locations
.pick_v4(gsm_inner, &ipv4.params.prefix)? .pick_v4(gsm_inner, &ipv4.params.prefix)?
@ -257,7 +393,8 @@ impl BlueprintState {
}; };
let params = NetworkStateIpv4Params { let params = NetworkStateIpv4Params {
allocation: subnet, scope,
reserve,
super_net, super_net,
}; };
@ -320,7 +457,10 @@ impl BlueprintState {
None => None, 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(()) Ok(())
} }
@ -335,7 +475,11 @@ impl BlueprintState {
}; };
// Get addresses for network // Get addresses for network
let Some(NetworkLocation { subnet, super_net }) = ipv6 let Some(NetworkLocation {
scope,
reserve,
super_net,
}) = ipv6
.params .params
.locations .locations
.pick_v6(gsm_inner, &ipv6.params.prefix)? .pick_v6(gsm_inner, &ipv6.params.prefix)?
@ -346,7 +490,8 @@ impl BlueprintState {
}; };
let params = NetworkStateIpv6Params { let params = NetworkStateIpv6Params {
allocation: subnet, scope,
reserve,
super_net, super_net,
}; };
@ -409,7 +554,10 @@ impl BlueprintState {
None => None, 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(()) Ok(())
} }

View File

@ -13,7 +13,8 @@ pub enum NetworkLocationsList {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NetworkLocation<T> { pub struct NetworkLocation<T> {
pub subnet: T, pub scope: Vec<T>,
pub reserve: Vec<T>,
pub super_net: Option<NetworkStateId>, pub super_net: Option<NetworkStateId>,
} }
@ -35,37 +36,31 @@ impl NetworkLocationsList {
NetworkLocationsList::Allocations { allocations } => { NetworkLocationsList::Allocations { allocations } => {
// Get allocations which have subnets that would fit // Get allocations which have subnets that would fit
// our maximum requested prefix // 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 let allocation = gsm_inner
.allocations() .allocations()
.get(allocation_name) .get(allocation_name)
.expect("must exist"); .expect("must exist");
Ok(allocation if allocation.address_pool.can_allocate_v4(max_prefix)? {
.subnets Ok(Some(allocation.address_pool.clone()))
.subnet4 } else {
.as_ref() Ok(None)
.and_then(|subnet| subnet.filter(|p| p.prefix_len() <= max_prefix))) }
})? })?
else { else {
return Ok(None); return Ok(None);
}; };
// Pick an allocation // Pick an address pool
let subnets = gsm_inner.srng().weighted_choice_ref(&alloc_subnets); let mut address_pool = gsm_inner.srng().weighted_choice(address_pools);
// Pick a subnet
let net = *gsm_inner.srng().weighted_choice_ref(subnets);
// Pick a prefix length that would fit in the subnet // Pick a prefix length that would fit in the subnet
let opt_subnet = prefix let opt_subnet = prefix
.filter(|p| *p >= net.prefix_len()) .try_filter(|p| address_pool.can_allocate_v4(*p))?
.as_ref() .as_ref()
.map(|wl| { .map(|wl| {
let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(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, ()) address_pool.allocate_random_v4(gsm_inner.srng(), subnet_prefix, ())
}) })
.transpose()? .transpose()?
@ -74,7 +69,8 @@ impl NetworkLocationsList {
return Ok(None); return Ok(None);
}; };
Ok(Some(NetworkLocation { Ok(Some(NetworkLocation {
subnet, scope: vec![subnet],
reserve: Vec::new(),
super_net: None, super_net: None,
})) }))
} }
@ -126,7 +122,8 @@ impl NetworkLocationsList {
.set_state(super_network_state); .set_state(super_network_state);
Ok(Some(NetworkLocation { Ok(Some(NetworkLocation {
subnet, scope: vec![subnet],
reserve: Vec::new(),
super_net: Some(super_network_id), super_net: Some(super_network_id),
})) }))
} }
@ -150,37 +147,31 @@ impl NetworkLocationsList {
NetworkLocationsList::Allocations { allocations } => { NetworkLocationsList::Allocations { allocations } => {
// Get allocations which have subnets that would fit // Get allocations which have subnets that would fit
// our maximum requested prefix // 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 let allocation = gsm_inner
.allocations() .allocations()
.get(allocation_name) .get(allocation_name)
.expect("must exist"); .expect("must exist");
Ok(allocation if allocation.address_pool.can_allocate_v6(max_prefix)? {
.subnets Ok(Some(allocation.address_pool.clone()))
.subnet6 } else {
.as_ref() Ok(None)
.and_then(|subnet| subnet.filter(|p| p.prefix_len() <= max_prefix))) }
})? })?
else { else {
return Ok(None); return Ok(None);
}; };
// Pick an allocation // Pick an address pool
let subnets = gsm_inner.srng().weighted_choice_ref(&alloc_subnets); let mut address_pool = gsm_inner.srng().weighted_choice(address_pools);
// Pick a subnet
let net = *gsm_inner.srng().weighted_choice_ref(subnets);
// Pick a prefix length that would fit in the subnet // Pick a prefix length that would fit in the subnet
let opt_subnet = prefix let opt_subnet = prefix
.filter(|p| *p >= net.prefix_len()) .try_filter(|p| address_pool.can_allocate_v6(*p))?
.as_ref() .as_ref()
.map(|wl| { .map(|wl| {
let subnet_prefix = *gsm_inner.srng().weighted_choice_ref(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, ()) address_pool.allocate_random_v6(gsm_inner.srng(), subnet_prefix, ())
}) })
.transpose()? .transpose()?
@ -189,7 +180,8 @@ impl NetworkLocationsList {
return Ok(None); return Ok(None);
}; };
Ok(Some(NetworkLocation { Ok(Some(NetworkLocation {
subnet, scope: vec![subnet],
reserve: Vec::new(),
super_net: None, super_net: None,
})) }))
} }
@ -241,7 +233,8 @@ impl NetworkLocationsList {
.set_state(super_network_state); .set_state(super_network_state);
Ok(Some(NetworkLocation { Ok(Some(NetworkLocation {
subnet, scope: vec![subnet],
reserve: Vec::new(),
super_net: Some(super_network_id), super_net: Some(super_network_id),
})) }))
} }

View File

@ -44,7 +44,7 @@ struct NetworkStateModel {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NetworkStateModelParams { pub struct NetworkStateModelParams {
/// Network latency distribution /// Network latency distribution
pub latency: Option<config::Distribution>, pub latency: config::Distribution,
/// Distance simulation metric /// Distance simulation metric
pub distance: Option<config::Distance>, pub distance: Option<config::Distance>,
/// Packet loss probability /// Packet loss probability
@ -53,7 +53,8 @@ pub struct NetworkStateModelParams {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NetworkStateIpv4Params { pub struct NetworkStateIpv4Params {
pub allocation: Ipv4Net, pub scope: Vec<Ipv4Net>,
pub reserve: Vec<Ipv4Net>,
pub super_net: Option<NetworkStateId>, pub super_net: Option<NetworkStateId>,
} }
@ -65,7 +66,8 @@ struct NetworkStateIpv4 {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NetworkStateIpv6Params { pub struct NetworkStateIpv6Params {
pub allocation: Ipv6Net, pub scope: Vec<Ipv6Net>,
pub reserve: Vec<Ipv6Net>,
pub super_net: Option<NetworkStateId>, pub super_net: Option<NetworkStateId>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -126,7 +128,7 @@ impl NetworkState {
address_pool: AddressPool::new(), address_pool: AddressPool::new(),
model: NetworkStateModel { model: NetworkStateModel {
params: NetworkStateModelParams { params: NetworkStateModelParams {
latency: None, latency: config::Distribution::default(),
distance: None, distance: None,
loss: 0.0, loss: 0.0,
}, },
@ -144,9 +146,7 @@ impl NetworkState {
.get_state(generating_blueprint) .get_state(generating_blueprint)
.expect("must exist"); .expect("must exist");
blueprint_state.on_network_released(self.id()); blueprint_state.on_network_released(self.id());
gsm_inner gsm_inner.blueprint_states_mut().set_state(blueprint_state)
.blueprint_states_mut()
.set_state(blueprint_state)
} }
} }
@ -161,7 +161,9 @@ impl NetworkState {
&mut self, &mut self,
gsm_inner: &mut GlobalStateManagerInner, gsm_inner: &mut GlobalStateManagerInner,
) -> GlobalStateManagerResult<()> { ) -> GlobalStateManagerResult<()> {
let Some(ipv4) = self.fields.ipv4.clone() else { self.clear_ipv4_gateway(gsm_inner)?;
if self.fields.ipv4.is_none() {
return Ok(()); return Ok(());
}; };
@ -176,28 +178,6 @@ impl NetworkState {
GlobalStateManagerError::ResourceInUse(format!("{}-v4", self.debug_name())) 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 // Update fields
self.fields = Arc::new(NetworkStateFields { self.fields = Arc::new(NetworkStateFields {
ipv4: None, ipv4: None,
@ -208,88 +188,168 @@ impl NetworkState {
Ok(()) 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( pub fn set_ipv4(
&mut self, &mut self,
gsm_inner: &mut GlobalStateManagerInner, gsm_inner: &mut GlobalStateManagerInner,
params: NetworkStateIpv4Params, params: NetworkStateIpv4Params,
gateway_params: Option<NetworkStateIpv4GatewayParams>,
) -> GlobalStateManagerResult<()> { ) -> GlobalStateManagerResult<()> {
self.clear_ipv4(gsm_inner)?; self.clear_ipv4(gsm_inner)?;
let mut address_pool = self.fields.address_pool.clone(); let mut address_pool = self.fields.address_pool.clone();
address_pool.add_scope_v4(params.allocation); for scope in &params.scope {
address_pool.add_scope_v4(*scope);
}
for reserve in &params.reserve {
address_pool.reserve_allocation_v4(*reserve, None)?;
}
let gateway = match gateway_params { let ipv4 = NetworkStateIpv4 {
Some(gateway_params) => { params,
// Allocate or reserve an internal network address for the gateway gateway: None,
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 }; // 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 // Update fields
self.fields = Arc::new(NetworkStateFields { self.fields = Arc::new(NetworkStateFields {
@ -305,7 +365,9 @@ impl NetworkState {
&mut self, &mut self,
gsm_inner: &mut GlobalStateManagerInner, gsm_inner: &mut GlobalStateManagerInner,
) -> GlobalStateManagerResult<()> { ) -> GlobalStateManagerResult<()> {
let Some(ipv6) = self.fields.ipv6.clone() else { self.clear_ipv6_gateway(gsm_inner)?;
if self.fields.ipv6.is_none() {
return Ok(()); return Ok(());
}; };
@ -320,28 +382,6 @@ impl NetworkState {
GlobalStateManagerError::ResourceInUse(format!("{}-v6", self.debug_name())) 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 // Update fields
self.fields = Arc::new(NetworkStateFields { self.fields = Arc::new(NetworkStateFields {
ipv6: None, ipv6: None,
@ -351,88 +391,166 @@ impl NetworkState {
Ok(()) 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( pub fn set_ipv6(
&mut self, &mut self,
gsm_inner: &mut GlobalStateManagerInner, gsm_inner: &mut GlobalStateManagerInner,
params: NetworkStateIpv6Params, params: NetworkStateIpv6Params,
gateway_params: Option<NetworkStateIpv6GatewayParams>,
) -> GlobalStateManagerResult<()> { ) -> GlobalStateManagerResult<()> {
self.clear_ipv6(gsm_inner)?; self.clear_ipv6(gsm_inner)?;
let mut address_pool = self.fields.address_pool.clone(); let mut address_pool = self.fields.address_pool.clone();
address_pool.add_scope_v6(params.allocation); for scope in &params.scope {
address_pool.add_scope_v6(*scope);
let gateway = match gateway_params { }
Some(gateway_params) => { for reserve in &params.reserve {
// Allocate or reserve an internal network address for the gateway address_pool.reserve_allocation_v6(*reserve, None)?;
let internal_address = }
if let Some(internal_address) = gateway_params.internal_address { let ipv6 = NetworkStateIpv6 {
address_pool.reserve_allocation_v6( params,
Ipv6Net::new(internal_address, 128).expect("must succeed"), gateway: None,
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,
}; };
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 // Update fields
self.fields = Arc::new(NetworkStateFields { self.fields = Arc::new(NetworkStateFields {
@ -477,11 +595,14 @@ impl NetworkState {
opt_address: Option<Ipv4Addr>, opt_address: Option<Ipv4Addr>,
) -> GlobalStateManagerResult<Ifv4Addr> { ) -> GlobalStateManagerResult<Ifv4Addr> {
let net = self.allocate_subnet_v4(gsm_inner, owner_tag, opt_address, 32)?; 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 ip = net.addr();
let netmask = scope.netmask();
let ipv4 = self.fields.ipv4.as_ref().unwrap(); let broadcast = scope.broadcast();
let netmask = ipv4.params.allocation.netmask();
let broadcast = ipv4.params.allocation.broadcast();
let ifaddr = Ifv4Addr { let ifaddr = Ifv4Addr {
ip, ip,
@ -517,11 +638,8 @@ impl NetworkState {
net net
} else { } else {
// Get a random address if available // Get a random address if available
let Some(allocation) = address_pool.allocate_random_v4( let Some(allocation) =
gsm_inner.srng(), address_pool.allocate_random_v4(gsm_inner.srng(), prefix, owner_tag)?
prefix,
owner_tag,
)?
else { else {
return Err(GlobalStateManagerError::NoAllocation); return Err(GlobalStateManagerError::NoAllocation);
}; };
@ -585,11 +703,15 @@ impl NetworkState {
opt_address: Option<Ipv6Addr>, opt_address: Option<Ipv6Addr>,
) -> GlobalStateManagerResult<Ifv6Addr> { ) -> GlobalStateManagerResult<Ifv6Addr> {
let net = self.allocate_subnet_v6(gsm_inner, owner_tag, opt_address, 128)?; 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 ip = net.addr();
let netmask = ipv6.params.allocation.netmask(); let netmask = scope.netmask();
let broadcast = ipv6.params.allocation.broadcast(); let broadcast = scope.broadcast();
let ifaddr = Ifv6Addr { let ifaddr = Ifv6Addr {
ip, ip,
@ -625,11 +747,8 @@ impl NetworkState {
net net
} else { } else {
// Get a random address if available // Get a random address if available
let Some(allocation) = address_pool.allocate_random_v6( let Some(allocation) =
gsm_inner.srng(), address_pool.allocate_random_v6(gsm_inner.srng(), prefix, owner_tag)?
prefix,
owner_tag,
)?
else { else {
return Err(GlobalStateManagerError::NoAllocation); return Err(GlobalStateManagerError::NoAllocation);
}; };

View File

@ -65,20 +65,6 @@ impl<S: State> StateRegistry<S> {
} }
} }
pub fn get_or_create_by_name<F: FnOnce(StateId<S>, String) -> S>(
&mut self,
name: String,
create: F,
) -> StateId<S> {
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<S> { pub fn allocate_id(&mut self) -> StateId<S> {
// Allocate new internal id // Allocate new internal id
let state_id = self.free_state_ids.pop_back().unwrap_or_else(|| { let state_id = self.free_state_ids.pop_back().unwrap_or_else(|| {

View File

@ -14,6 +14,9 @@ default_network: "$internet"
# this is '$lan') # this is '$lan')
default_model: "$lan" default_model: "$lan"
# The name of the default allocation pool that subnets are allocated from
default_pool: "$internet"
################################################################# #################################################################
# Networks # Networks
# #
@ -38,54 +41,56 @@ networks:
# will be used (on the 'public internet'). # will be used (on the 'public internet').
allocations: allocations:
# Predefined networks # Predefined allocations
$internet: {} $internet:
scope4: ["0.0.0.0/0"]
scope6: ["::/0"]
$private: $private:
subnet4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] scope4: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
subnet6: ["fc00::/7"] scope6: ["fc00::/7"]
$cgnat: $cgnat:
subnet4: ["100.64.0.0/10"] scope4: ["100.64.0.0/10"]
$linklocal: $linklocal:
subnet4: ["169.254.0.0/16"] scope4: ["169.254.0.0/16"]
subnet6: ["fe80::/10"] scope6: ["fe80::/10"]
$localhost: $localhost:
subnet4: ["127.0.0.0/8"] scope4: ["127.0.0.0/8"]
subnet6: ["::1/128"] scope6: ["::1/128"]
$ietf: $ietf:
subnet4: ["192.0.0.0/24"] scope4: ["192.0.0.0/24"]
$cellnat: $cellnat:
subnet4: ["192.0.0.0/29"] scope4: ["192.0.0.0/29"]
$documentation: $documentation:
subnet4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"] scope4: ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
subnet6: ["2001:db8::/32", "3fff::/20"] scope6: ["2001:db8::/32", "3fff::/20"]
$benchmark: $benchmark:
subnet4: ["198.18.0.0/15"] scope4: ["198.18.0.0/15"]
$mulitcast: $mulitcast:
subnet4: ["224.0.0.0/4"] scope4: ["224.0.0.0/4"]
$mulitcasttest: $mulitcasttest:
subnet4: ["233.252.0.0/24"] scope4: ["233.252.0.0/24"]
subnet6: ["ff00::/8"] scope6: ["ff00::/8"]
$unspecified: $unspecified:
subnet4: ["0.0.0.0/8"] scope4: ["0.0.0.0/8"]
subnet6: ["::/128"] scope6: ["::/128"]
$reserved: $reserved:
subnet4: ["192.88.99.0/24", "240.0.0.0/4"] scope4: ["192.88.99.0/24", "240.0.0.0/4"]
$broadcast: $broadcast:
subnet4: ["255.255.255.255/32"] scope4: ["255.255.255.255/32"]
$mapped: $mapped:
subnet6: ["::ffff:0:0/96", "::ffff:0:0:0/96"] scope6: ["::ffff:0:0/96", "::ffff:0:0:0/96"]
$translation: $translation:
subnet6: ["64:ff9b::/96", "64:ff9b:1::/48"] scope6: ["64:ff9b::/96", "64:ff9b:1::/48"]
$discard: $discard:
subnet6: ["100::/64"] scope6: ["100::/64"]
$teredo: $teredo:
subnet6: ["2001::/32"] scope6: ["2001::/32"]
$orchidv2: $orchidv2:
subnet6: ["2001:20::/28"] scope6: ["2001:20::/28"]
$6to4: $6to4:
subnet6: ["2002::/16"] scope6: ["2002::/16"]
$srv6: $srv6:
subnet6: ["5f00::/16"] scope6: ["5f00::/16"]
################################################################# #################################################################
# Models # Models

View File

@ -156,6 +156,34 @@ impl<T: fmt::Debug + Clone> WeightedList<T> {
} }
} }
pub fn try_map<F, S, E>(&self, mut filter: F) -> Result<WeightedList<S>, E>
where
F: FnMut(&T) -> Result<S, E>,
S: fmt::Debug + Clone,
{
match self {
WeightedList::Single(v) => {
let item = filter(v)?;
Ok(WeightedList::Single(item))
}
WeightedList::List(vec) => {
let mut out = Vec::<Weighted<S>>::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> { pub fn iter(&self) -> WeightedListIter<'_, T> {
WeightedListIter { WeightedListIter {
values: self, values: self,