mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-03-15 10:26:33 -04:00
[skip ci] blueprint config execution
This commit is contained in:
parent
54005b1aec
commit
a5846af804
@ -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(
|
||||
|
@ -120,10 +120,10 @@ pub struct TemplateLimits {
|
||||
/// maximum number of machines this template will generate
|
||||
#[validate(nested)]
|
||||
#[serde(default)]
|
||||
pub machine_count: Option<WeightedList<u32>>,
|
||||
pub machine_count: Option<WeightedList<usize>>,
|
||||
#[validate(nested)]
|
||||
#[serde(default)]
|
||||
pub machines_per_network: Option<WeightedList<u32>>,
|
||||
pub machines_per_network: Option<WeightedList<usize>>,
|
||||
}
|
||||
|
||||
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<WeightedList<u32>>,
|
||||
pub network_count: Option<WeightedList<usize>>,
|
||||
}
|
||||
|
||||
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<u8>,
|
||||
#[serde(default)]
|
||||
@ -354,29 +354,27 @@ pub struct BlueprintGateway {
|
||||
pub translation: WeightedList<Translation>,
|
||||
#[validate(range(min = 0.0, max = 1.0))]
|
||||
pub upnp: Probability,
|
||||
#[serde(flatten)]
|
||||
pub location: TemplateLocation,
|
||||
#[serde(default, flatten)]
|
||||
#[validate(nested)]
|
||||
pub location: Option<TemplateLocation>,
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
|
||||
#[validate(schema(function = "validate_subnets"))]
|
||||
pub struct Subnets {
|
||||
pub struct Scope4 {
|
||||
#[validate(length(min = 1))]
|
||||
pub scope4: Vec<Ipv4Net>,
|
||||
#[serde(default)]
|
||||
#[validate(nested)]
|
||||
pub subnet4: Option<WeightedList<Ipv4Net>>,
|
||||
#[serde(default)]
|
||||
#[validate(nested)]
|
||||
pub subnet6: Option<WeightedList<Ipv6Net>>,
|
||||
pub pool4: Option<String>,
|
||||
}
|
||||
|
||||
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<Ipv6Net>,
|
||||
#[serde(default)]
|
||||
pub pool6: Option<String>,
|
||||
}
|
||||
|
||||
#[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<Scope4>,
|
||||
#[serde(flatten)]
|
||||
#[validate(nested)]
|
||||
pub scope6: Option<Scope6>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
@ -467,6 +468,8 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub default_model: Option<String>,
|
||||
#[serde(default)]
|
||||
pub default_pool: Option<String>,
|
||||
#[serde(default)]
|
||||
pub profiles: HashMap<String, Profile>,
|
||||
#[serde(default)]
|
||||
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("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(),
|
||||
|
@ -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
|
||||
#
|
||||
|
@ -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) {
|
||||
let mut scopes = self.scope_v4.iter().copied().collect::<Vec<_>>();
|
||||
scopes.push(allocation);
|
||||
@ -40,22 +53,22 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
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 {
|
||||
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 {
|
||||
if x.contains(&allocation) {
|
||||
return true;
|
||||
return Some(*x);
|
||||
}
|
||||
}
|
||||
false
|
||||
None
|
||||
}
|
||||
|
||||
pub fn can_allocate_v6(&self, prefix: u8) -> GlobalStateManagerResult<bool> {
|
||||
@ -82,11 +95,11 @@ impl<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
&mut self,
|
||||
allocation: Ipv4Net,
|
||||
opt_tag: Option<T>,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
) -> GlobalStateManagerResult<Ipv4Net> {
|
||||
// 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<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
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<T>,
|
||||
) -> GlobalStateManagerResult<()> {
|
||||
) -> GlobalStateManagerResult<Ipv6Net> {
|
||||
// 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<T: fmt::Debug + Clone> AddressPool<T> {
|
||||
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<Ipv4Net> {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<T> = Result<T, GlobalStateManagerError>;
|
||||
|
@ -69,8 +69,8 @@ pub struct BlueprintState {
|
||||
pub type BlueprintStateId = StateId<BlueprintState>;
|
||||
|
||||
impl BlueprintState {
|
||||
pub fn new(id: BlueprintStateId, name: String) -> GlobalStateManagerResult<BlueprintState> {
|
||||
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<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(
|
||||
&mut self,
|
||||
gsm_inner: &mut GlobalStateManagerInner,
|
||||
params: BlueprintStateIpv4Params,
|
||||
gateway_params: Option<BlueprintStateGatewayParams>,
|
||||
) {
|
||||
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<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 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<BlueprintStateGatewayParams>,
|
||||
) {
|
||||
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<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 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(())
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,8 @@ pub enum NetworkLocationsList {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkLocation<T> {
|
||||
pub subnet: T,
|
||||
pub scope: Vec<T>,
|
||||
pub reserve: Vec<T>,
|
||||
pub super_net: Option<NetworkStateId>,
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}))
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ struct NetworkStateModel {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkStateModelParams {
|
||||
/// Network latency distribution
|
||||
pub latency: Option<config::Distribution>,
|
||||
pub latency: config::Distribution,
|
||||
/// Distance simulation metric
|
||||
pub distance: Option<config::Distance>,
|
||||
/// Packet loss probability
|
||||
@ -53,7 +53,8 @@ pub struct NetworkStateModelParams {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkStateIpv4Params {
|
||||
pub allocation: Ipv4Net,
|
||||
pub scope: Vec<Ipv4Net>,
|
||||
pub reserve: Vec<Ipv4Net>,
|
||||
pub super_net: Option<NetworkStateId>,
|
||||
}
|
||||
|
||||
@ -65,7 +66,8 @@ struct NetworkStateIpv4 {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkStateIpv6Params {
|
||||
pub allocation: Ipv6Net,
|
||||
pub scope: Vec<Ipv6Net>,
|
||||
pub reserve: Vec<Ipv6Net>,
|
||||
pub super_net: Option<NetworkStateId>,
|
||||
}
|
||||
#[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<NetworkStateIpv4GatewayParams>,
|
||||
) -> 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<NetworkStateIpv6GatewayParams>,
|
||||
) -> 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<Ipv4Addr>,
|
||||
) -> GlobalStateManagerResult<Ifv4Addr> {
|
||||
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<Ipv6Addr>,
|
||||
) -> GlobalStateManagerResult<Ifv6Addr> {
|
||||
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);
|
||||
};
|
||||
|
@ -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> {
|
||||
// Allocate new internal id
|
||||
let state_id = self.free_state_ids.pop_back().unwrap_or_else(|| {
|
||||
|
@ -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
|
||||
|
@ -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> {
|
||||
WeightedListIter {
|
||||
values: self,
|
||||
|
Loading…
x
Reference in New Issue
Block a user