snp-daemon/src/config.rs

180 lines
5.3 KiB
Rust

// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: Unlicense
use anyhow::Result;
use serde::Deserialize;
use std::{
net::{Ipv4Addr, Ipv6Addr},
ops::Range,
};
#[derive(Deserialize, Debug)]
pub struct IPv4Range {
pub first_ip: Ipv4Addr,
pub last_ip: Ipv4Addr,
pub netmask: String,
pub gateway: Ipv4Addr,
}
#[derive(Deserialize, Debug)]
pub struct IPv6Range {
pub first_ip: Ipv6Addr,
pub last_ip: Ipv6Addr,
pub netmask: String,
pub gateway: Ipv6Addr,
}
#[derive(Deserialize, Debug)]
pub struct Interface {
pub driver: InterfaceType,
pub device: String,
pub ipv4_ranges: Vec<IPv4Range>,
pub ipv6_ranges: Vec<IPv6Range>,
}
#[derive(Deserialize, Debug)]
pub enum InterfaceType {
MACVTAP,
IPVTAP,
Bridge,
}
#[derive(Deserialize, Debug)]
pub struct Offer {
// price per unit per minute
pub price: u64,
pub max_vcpu_reservation: usize,
pub max_mem_reservation_mib: usize,
pub storage_path: String,
pub max_storage_mib: usize,
}
#[derive(Deserialize, Debug)]
pub struct Config {
pub owner_wallet: String,
pub network: String,
pub network_interfaces: Vec<Interface>,
pub offers: Vec<Offer>,
#[serde(with = "range_format")]
pub public_port_range: Range<u16>,
pub max_ports_per_vm: u16,
}
mod range_format {
use serde::{Deserialize, Deserializer, Serialize};
use std::ops::Range;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Range<u16>, D::Error>
where
D: Deserializer<'de>,
{
let range_repr = RangeRepr::deserialize(deserializer)?;
Ok(range_repr.start..range_repr.end)
}
#[derive(Serialize, Deserialize)]
struct RangeRepr {
start: u16,
end: u16,
}
}
impl Config {
pub fn load_from_disk(path: &str) -> Result<Self> {
let content = std::fs::read_to_string(path)?;
let config: Config = serde_yaml::from_str(&content)?;
let offers_path_set: std::collections::HashSet<String> =
config.offers.iter().map(|offer| offer.storage_path.clone()).collect();
if offers_path_set.len() != config.offers.len() {
return Err(anyhow::anyhow!(
"Each offer must have a unique folder for saving VM disk files."
));
}
for nic in &config.network_interfaces {
for range in &nic.ipv4_ranges {
let ipv4_netmask = range.netmask.parse::<u8>()?;
if ipv4_netmask > 32 {
return Err(anyhow::anyhow!(
"IPv4 netmask must be in short format: a number from 1 to 32"
));
}
if range.first_ip.to_bits() > range.last_ip.to_bits() {
return Err(anyhow::anyhow!(
"For range {range:?} first ip is bigger than last ip."
));
}
let expected_netmask = std::cmp::min(
calc_ipv4_netmask(range.first_ip, range.gateway),
calc_ipv4_netmask(range.last_ip, range.gateway),
);
if expected_netmask < ipv4_netmask as u32 {
return Err(anyhow::anyhow!(
"Your netmask is too small to include the IPs and also the gateway: {range:?}"
));
}
}
for range in &nic.ipv6_ranges {
let ipv6_netmask = range.netmask.parse::<u8>()?;
if ipv6_netmask > 128 {
return Err(anyhow::anyhow!(
"IPv6 netmask must be in short format: a number from 1 to 128"
));
}
if range.first_ip.to_bits() > range.last_ip.to_bits() {
return Err(anyhow::anyhow!(
"For range {range:?} first ip is bigger than last ip."
));
}
let expected_netmask = std::cmp::min(
calc_ipv6_netmask(range.first_ip, range.gateway),
calc_ipv6_netmask(range.last_ip, range.gateway),
);
if expected_netmask < ipv6_netmask as u128 {
return Err(anyhow::anyhow!(
"Your netmask is too small to include the IPs and also the gateway: {range:?}"
));
}
}
}
Ok(config)
}
}
fn calc_ipv4_netmask(ip: Ipv4Addr, gateway: Ipv4Addr) -> u32 {
// Convert the IPs to u32 for easier bit manipulation
let ip_u32 = u32::from(ip);
let gateway_u32 = u32::from(gateway);
// Find the smallest common prefix
let mut prefix_len = 0;
for i in 1..=32 {
if (ip_u32 >> (32 - i)) == (gateway_u32 >> (32 - i)) {
prefix_len = i;
} else {
break;
}
}
// Return the mask as a string
prefix_len
}
fn calc_ipv6_netmask(ip: Ipv6Addr, gateway: Ipv6Addr) -> u128 {
// Convert the IPs to u128 for easier bit manipulation
let ip_u128 = u128::from(ip);
let gateway_u128 = u128::from(gateway);
// Find the smallest common prefix
let mut prefix_len = 0;
for i in 1..=128 {
if (ip_u128 >> (128 - i)) == (gateway_u128 >> (128 - i)) {
prefix_len = i;
} else {
break;
}
}
// Return the mask as a string
prefix_len
}