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}