kratanet/
autonet.rs

1use anyhow::Result;
2use krata::{
3    events::EventStream,
4    v1::{
5        common::Zone,
6        control::{
7            control_service_client::ControlServiceClient, watch_events_reply::Event,
8            ListZonesRequest,
9        },
10    },
11};
12use log::warn;
13use smoltcp::wire::{EthernetAddress, Ipv4Cidr, Ipv6Cidr};
14use std::{collections::HashMap, str::FromStr, time::Duration};
15use tokio::{select, sync::broadcast::Receiver, time::sleep};
16use tonic::transport::Channel;
17use uuid::Uuid;
18
19pub struct AutoNetworkWatcher {
20    control: ControlServiceClient<Channel>,
21    pub events: EventStream,
22    known: HashMap<Uuid, NetworkMetadata>,
23}
24
25#[derive(Debug, Clone)]
26pub struct NetworkSide {
27    pub ipv4: Ipv4Cidr,
28    pub ipv6: Ipv6Cidr,
29    pub mac: EthernetAddress,
30}
31
32#[derive(Debug, Clone)]
33pub struct NetworkMetadata {
34    pub domid: u32,
35    pub uuid: Uuid,
36    pub zone: NetworkSide,
37    pub gateway: NetworkSide,
38}
39
40impl NetworkMetadata {
41    pub fn interface(&self) -> String {
42        format!("vif{}.20", self.domid)
43    }
44}
45
46#[derive(Debug, Clone)]
47pub struct AutoNetworkChangeset {
48    pub added: Vec<NetworkMetadata>,
49    pub removed: Vec<NetworkMetadata>,
50}
51
52impl AutoNetworkWatcher {
53    pub async fn new(control: ControlServiceClient<Channel>) -> Result<AutoNetworkWatcher> {
54        let client = control.clone();
55        Ok(AutoNetworkWatcher {
56            control,
57            events: EventStream::open(client).await?,
58            known: HashMap::new(),
59        })
60    }
61
62    pub async fn read(&mut self) -> Result<Vec<NetworkMetadata>> {
63        let mut all_zones: HashMap<Uuid, Zone> = HashMap::new();
64        for zone in self
65            .control
66            .list_zones(ListZonesRequest {})
67            .await?
68            .into_inner()
69            .zones
70        {
71            let Ok(uuid) = Uuid::from_str(&zone.id) else {
72                continue;
73            };
74            all_zones.insert(uuid, zone);
75        }
76
77        let mut networks: Vec<NetworkMetadata> = Vec::new();
78        for (uuid, zone) in &all_zones {
79            let Some(ref status) = zone.status else {
80                continue;
81            };
82
83            if status.domid == u32::MAX {
84                continue;
85            }
86
87            let Some(ref network_status) = status.network_status else {
88                continue;
89            };
90
91            let Ok(zone_ipv4_cidr) = Ipv4Cidr::from_str(&network_status.zone_ipv4) else {
92                continue;
93            };
94
95            let Ok(zone_ipv6_cidr) = Ipv6Cidr::from_str(&network_status.zone_ipv6) else {
96                continue;
97            };
98
99            let Ok(zone_mac) = EthernetAddress::from_str(&network_status.zone_mac) else {
100                continue;
101            };
102
103            let Ok(gateway_ipv4_cidr) = Ipv4Cidr::from_str(&network_status.gateway_ipv4) else {
104                continue;
105            };
106
107            let Ok(gateway_ipv6_cidr) = Ipv6Cidr::from_str(&network_status.gateway_ipv6) else {
108                continue;
109            };
110
111            let Ok(gateway_mac) = EthernetAddress::from_str(&network_status.gateway_mac) else {
112                continue;
113            };
114
115            networks.push(NetworkMetadata {
116                domid: status.domid,
117                uuid: *uuid,
118                zone: NetworkSide {
119                    ipv4: zone_ipv4_cidr,
120                    ipv6: zone_ipv6_cidr,
121                    mac: zone_mac,
122                },
123                gateway: NetworkSide {
124                    ipv4: gateway_ipv4_cidr,
125                    ipv6: gateway_ipv6_cidr,
126                    mac: gateway_mac,
127                },
128            });
129        }
130        Ok(networks)
131    }
132
133    pub async fn read_changes(&mut self) -> Result<AutoNetworkChangeset> {
134        let mut seen: Vec<Uuid> = Vec::new();
135        let mut added: Vec<NetworkMetadata> = Vec::new();
136        let mut removed: Vec<NetworkMetadata> = Vec::new();
137
138        let networks = match self.read().await {
139            Ok(networks) => networks,
140            Err(error) => {
141                warn!("failed to read network changes: {}", error);
142                return Ok(AutoNetworkChangeset { added, removed });
143            }
144        };
145
146        for network in networks {
147            seen.push(network.uuid);
148            if self.known.contains_key(&network.uuid) {
149                continue;
150            }
151            let _ = self.known.insert(network.uuid, network.clone());
152            added.push(network);
153        }
154
155        let mut gone: Vec<Uuid> = Vec::new();
156        for uuid in self.known.keys() {
157            if seen.contains(uuid) {
158                continue;
159            }
160            gone.push(*uuid);
161        }
162
163        for uuid in &gone {
164            let Some(network) = self.known.remove(uuid) else {
165                continue;
166            };
167
168            removed.push(network);
169        }
170
171        Ok(AutoNetworkChangeset { added, removed })
172    }
173
174    pub async fn wait(&mut self, receiver: &mut Receiver<Event>) -> Result<()> {
175        loop {
176            select! {
177                x = receiver.recv() => match x {
178                    Ok(Event::ZoneChanged(_)) => {
179                        break;
180                    },
181
182                    Err(error) => {
183                        warn!("failed to receive event: {}", error);
184                    }
185                },
186
187                _ = sleep(Duration::from_secs(10)) => {
188                    break;
189                }
190            }
191        }
192        Ok(())
193    }
194
195    pub fn mark_unknown(&mut self, uuid: Uuid) -> Result<bool> {
196        Ok(self.known.remove(&uuid).is_some())
197    }
198}