180 lines
5.3 KiB
Rust
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
|
|
}
|