2024-02-12 14:24:38 +00:00
|
|
|
use anyhow::{anyhow, Result};
|
|
|
|
use smoltcp::wire::{EthernetAddress, Ipv4Cidr, Ipv6Cidr};
|
|
|
|
use std::{collections::HashMap, str::FromStr};
|
|
|
|
use uuid::Uuid;
|
2024-03-08 13:08:59 +00:00
|
|
|
use xenstore::{XsdClient, XsdInterface, XsdTransaction};
|
2024-02-12 14:24:38 +00:00
|
|
|
|
|
|
|
pub struct AutoNetworkCollector {
|
|
|
|
client: XsdClient,
|
|
|
|
known: HashMap<Uuid, NetworkMetadata>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct NetworkSide {
|
|
|
|
pub ipv4: Ipv4Cidr,
|
|
|
|
pub ipv6: Ipv6Cidr,
|
|
|
|
pub mac: EthernetAddress,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct NetworkMetadata {
|
|
|
|
pub domid: u32,
|
|
|
|
pub uuid: Uuid,
|
|
|
|
pub guest: NetworkSide,
|
|
|
|
pub gateway: NetworkSide,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NetworkMetadata {
|
|
|
|
pub fn interface(&self) -> String {
|
|
|
|
format!("vif{}.20", self.domid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct AutoNetworkChangeset {
|
|
|
|
pub added: Vec<NetworkMetadata>,
|
|
|
|
pub removed: Vec<NetworkMetadata>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AutoNetworkCollector {
|
2024-02-23 04:37:53 +00:00
|
|
|
pub async fn new() -> Result<AutoNetworkCollector> {
|
2024-02-12 14:24:38 +00:00
|
|
|
Ok(AutoNetworkCollector {
|
2024-02-23 04:37:53 +00:00
|
|
|
client: XsdClient::open().await?,
|
2024-02-12 14:24:38 +00:00
|
|
|
known: HashMap::new(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-23 04:37:53 +00:00
|
|
|
pub async fn read(&mut self) -> Result<Vec<NetworkMetadata>> {
|
2024-02-12 14:24:38 +00:00
|
|
|
let mut networks = Vec::new();
|
2024-03-07 04:14:25 -08:00
|
|
|
let tx = self.client.transaction().await?;
|
2024-02-23 04:37:53 +00:00
|
|
|
for domid_string in tx.list("/local/domain").await? {
|
2024-02-12 14:24:38 +00:00
|
|
|
let Ok(domid) = domid_string.parse::<u32>() else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
let dom_path = format!("/local/domain/{}", domid_string);
|
2024-02-23 04:37:53 +00:00
|
|
|
let Some(uuid_string) = tx.read_string(&format!("{}/krata/uuid", dom_path)).await?
|
2024-02-12 14:24:38 +00:00
|
|
|
else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
let Ok(uuid) = uuid_string.parse::<Uuid>() else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
let Ok(guest) =
|
2024-03-07 04:14:25 -08:00
|
|
|
AutoNetworkCollector::read_network_side(uuid, &tx, &dom_path, "guest").await
|
2024-02-12 14:24:38 +00:00
|
|
|
else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
let Ok(gateway) =
|
2024-03-07 04:14:25 -08:00
|
|
|
AutoNetworkCollector::read_network_side(uuid, &tx, &dom_path, "gateway").await
|
2024-02-12 14:24:38 +00:00
|
|
|
else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
networks.push(NetworkMetadata {
|
|
|
|
domid,
|
|
|
|
uuid,
|
|
|
|
guest,
|
|
|
|
gateway,
|
|
|
|
});
|
|
|
|
}
|
2024-02-23 04:37:53 +00:00
|
|
|
tx.commit().await?;
|
2024-02-12 14:24:38 +00:00
|
|
|
Ok(networks)
|
|
|
|
}
|
|
|
|
|
2024-02-23 04:37:53 +00:00
|
|
|
async fn read_network_side(
|
2024-02-12 14:24:38 +00:00
|
|
|
uuid: Uuid,
|
2024-03-07 04:14:25 -08:00
|
|
|
tx: &XsdTransaction,
|
2024-02-12 14:24:38 +00:00
|
|
|
dom_path: &str,
|
|
|
|
side: &str,
|
|
|
|
) -> Result<NetworkSide> {
|
2024-02-21 20:57:46 +00:00
|
|
|
let side_path = format!("{}/krata/network/{}", dom_path, side);
|
2024-02-23 04:37:53 +00:00
|
|
|
let Some(ipv4) = tx.read_string(&format!("{}/ipv4", side_path)).await? else {
|
2024-02-12 14:24:38 +00:00
|
|
|
return Err(anyhow!(
|
2024-02-21 20:57:46 +00:00
|
|
|
"krata domain {} is missing {} ipv4 network entry",
|
2024-02-12 14:24:38 +00:00
|
|
|
uuid,
|
|
|
|
side
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
2024-02-23 04:37:53 +00:00
|
|
|
let Some(ipv6) = tx.read_string(&format!("{}/ipv6", side_path)).await? else {
|
2024-02-12 14:24:38 +00:00
|
|
|
return Err(anyhow!(
|
2024-02-21 20:57:46 +00:00
|
|
|
"krata domain {} is missing {} ipv6 network entry",
|
2024-02-12 14:24:38 +00:00
|
|
|
uuid,
|
|
|
|
side
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
2024-02-23 04:37:53 +00:00
|
|
|
let Some(mac) = tx.read_string(&format!("{}/mac", side_path)).await? else {
|
2024-02-12 14:24:38 +00:00
|
|
|
return Err(anyhow!(
|
2024-02-21 20:57:46 +00:00
|
|
|
"krata domain {} is missing {} mac address entry",
|
2024-02-12 14:24:38 +00:00
|
|
|
uuid,
|
|
|
|
side
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
|
|
|
let Ok(ipv4) = Ipv4Cidr::from_str(&ipv4) else {
|
|
|
|
return Err(anyhow!(
|
2024-02-21 20:57:46 +00:00
|
|
|
"krata domain {} has invalid {} ipv4 network cidr entry: {}",
|
2024-02-12 14:24:38 +00:00
|
|
|
uuid,
|
|
|
|
side,
|
|
|
|
ipv4
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
|
|
|
let Ok(ipv6) = Ipv6Cidr::from_str(&ipv6) else {
|
|
|
|
return Err(anyhow!(
|
2024-02-21 20:57:46 +00:00
|
|
|
"krata domain {} has invalid {} ipv6 network cidr entry: {}",
|
2024-02-12 14:24:38 +00:00
|
|
|
uuid,
|
|
|
|
side,
|
|
|
|
ipv6
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
|
|
|
let Ok(mac) = EthernetAddress::from_str(&mac) else {
|
|
|
|
return Err(anyhow!(
|
2024-02-21 20:57:46 +00:00
|
|
|
"krata domain {} has invalid {} mac address entry: {}",
|
2024-02-12 14:24:38 +00:00
|
|
|
uuid,
|
|
|
|
side,
|
|
|
|
mac
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(NetworkSide { ipv4, ipv6, mac })
|
|
|
|
}
|
|
|
|
|
2024-02-23 04:37:53 +00:00
|
|
|
pub async fn read_changes(&mut self) -> Result<AutoNetworkChangeset> {
|
2024-02-12 14:24:38 +00:00
|
|
|
let mut seen: Vec<Uuid> = Vec::new();
|
|
|
|
let mut added: Vec<NetworkMetadata> = Vec::new();
|
|
|
|
let mut removed: Vec<NetworkMetadata> = Vec::new();
|
|
|
|
|
2024-02-23 04:37:53 +00:00
|
|
|
for network in self.read().await? {
|
2024-02-12 14:24:38 +00:00
|
|
|
seen.push(network.uuid);
|
|
|
|
if self.known.contains_key(&network.uuid) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let _ = self.known.insert(network.uuid, network.clone());
|
|
|
|
added.push(network);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut gone: Vec<Uuid> = Vec::new();
|
|
|
|
for uuid in self.known.keys() {
|
|
|
|
if seen.contains(uuid) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
gone.push(*uuid);
|
|
|
|
}
|
|
|
|
|
|
|
|
for uuid in &gone {
|
|
|
|
let Some(network) = self.known.remove(uuid) else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
removed.push(network);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(AutoNetworkChangeset { added, removed })
|
|
|
|
}
|
2024-02-13 10:03:28 +00:00
|
|
|
|
|
|
|
pub fn mark_unknown(&mut self, uuid: Uuid) -> Result<bool> {
|
|
|
|
Ok(self.known.remove(&uuid).is_some())
|
|
|
|
}
|
2024-02-12 14:24:38 +00:00
|
|
|
}
|