1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct MsbnetReady {
19 pub pid: u32,
21
22 pub backend: String,
24
25 pub ifname: String,
27
28 pub guest_iface: String,
30
31 pub mac: String,
33
34 pub mtu: u16,
36
37 pub ipv4: Option<MsbnetReadyIpv4>,
39
40 pub ipv6: Option<MsbnetReadyIpv6>,
42
43 #[serde(default)]
45 pub tls: Option<MsbnetReadyTls>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct MsbnetReadyTls {
51 pub enabled: bool,
53
54 pub proxy_port: u16,
56
57 pub ca_pem: String,
59
60 pub intercepted_ports: Vec<u16>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct MsbnetReadyIpv4 {
67 pub address: String,
69
70 pub prefix_len: u8,
72
73 pub gateway: String,
75
76 pub dns: Vec<String>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct MsbnetReadyIpv6 {
83 pub address: String,
85
86 pub prefix_len: u8,
88
89 pub gateway: String,
91
92 pub dns: Vec<String>,
94}
95
96impl MsbnetReady {
101 pub fn to_env_vars(&self) -> Vec<(&'static str, String)> {
106 use std::fmt::Write;
107
108 let mut vars = Vec::with_capacity(3);
109
110 let net = format!(
112 "iface={},mac={},mtu={}",
113 self.guest_iface, self.mac, self.mtu
114 );
115 vars.push((microsandbox_protocol::ENV_NET, net));
116
117 if let Some(ipv4) = &self.ipv4 {
119 let mut val = format!(
120 "addr={}/{},gw={}",
121 ipv4.address, ipv4.prefix_len, ipv4.gateway
122 );
123 if let Some(dns) = ipv4.dns.first() {
126 let _ = write!(val, ",dns={dns}");
127 }
128 vars.push((microsandbox_protocol::ENV_NET_IPV4, val));
129 }
130
131 if let Some(ipv6) = &self.ipv6 {
133 let mut val = format!(
134 "addr={}/{},gw={}",
135 ipv6.address, ipv6.prefix_len, ipv6.gateway
136 );
137 if let Some(dns) = ipv6.dns.first() {
138 let _ = write!(val, ",dns={dns}");
139 }
140 vars.push((microsandbox_protocol::ENV_NET_IPV6, val));
141 }
142
143 vars
144 }
145}
146
147#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_msbnet_ready_serde_roundtrip() {
157 let ready = MsbnetReady {
158 pid: 12345,
159 backend: "linux_tap".to_string(),
160 ifname: "msbtap42".to_string(),
161 guest_iface: "eth0".to_string(),
162 mac: "02:5a:7b:13:01:02".to_string(),
163 mtu: 1500,
164 ipv4: Some(MsbnetReadyIpv4 {
165 address: "100.96.1.2".to_string(),
166 prefix_len: 30,
167 gateway: "100.96.1.1".to_string(),
168 dns: vec!["100.96.1.1".to_string()],
169 }),
170 ipv6: Some(MsbnetReadyIpv6 {
171 address: "fd42:6d73:62:2a::2".to_string(),
172 prefix_len: 64,
173 gateway: "fd42:6d73:62:2a::1".to_string(),
174 dns: vec!["fd42:6d73:62:2a::1".to_string()],
175 }),
176 tls: None,
177 };
178
179 let json = serde_json::to_string(&ready).unwrap();
180 let decoded: MsbnetReady = serde_json::from_str(&json).unwrap();
181
182 assert_eq!(decoded.pid, 12345);
183 assert_eq!(decoded.backend, "linux_tap");
184 assert_eq!(decoded.ifname, "msbtap42");
185 assert_eq!(decoded.guest_iface, "eth0");
186 assert_eq!(decoded.mtu, 1500);
187 assert!(decoded.ipv4.is_some());
188 assert!(decoded.ipv6.is_some());
189 }
190
191 #[test]
192 fn test_msbnet_ready_to_env_vars_dual_stack() {
193 let ready = MsbnetReady {
194 pid: 1,
195 backend: "linux_tap".to_string(),
196 ifname: "msbtap42".to_string(),
197 guest_iface: "eth0".to_string(),
198 mac: "02:5a:7b:13:01:02".to_string(),
199 mtu: 1500,
200 ipv4: Some(MsbnetReadyIpv4 {
201 address: "100.96.1.2".to_string(),
202 prefix_len: 30,
203 gateway: "100.96.1.1".to_string(),
204 dns: vec!["100.96.1.1".to_string()],
205 }),
206 ipv6: Some(MsbnetReadyIpv6 {
207 address: "fd42:6d73:62:2a::2".to_string(),
208 prefix_len: 64,
209 gateway: "fd42:6d73:62:2a::1".to_string(),
210 dns: vec!["fd42:6d73:62:2a::1".to_string()],
211 }),
212 tls: None,
213 };
214
215 let vars = ready.to_env_vars();
216 assert_eq!(vars.len(), 3);
217 assert_eq!(vars[0].0, "MSB_NET");
218 assert_eq!(vars[0].1, "iface=eth0,mac=02:5a:7b:13:01:02,mtu=1500");
219 assert_eq!(vars[1].0, "MSB_NET_IPV4");
220 assert_eq!(vars[1].1, "addr=100.96.1.2/30,gw=100.96.1.1,dns=100.96.1.1");
221 assert_eq!(vars[2].0, "MSB_NET_IPV6");
222 assert_eq!(
223 vars[2].1,
224 "addr=fd42:6d73:62:2a::2/64,gw=fd42:6d73:62:2a::1,dns=fd42:6d73:62:2a::1"
225 );
226 }
227
228 #[test]
229 fn test_msbnet_ready_to_env_vars_ipv4_only() {
230 let ready = MsbnetReady {
231 pid: 1,
232 backend: "linux_tap".to_string(),
233 ifname: "msbtap0".to_string(),
234 guest_iface: "eth0".to_string(),
235 mac: "02:00:00:00:00:01".to_string(),
236 mtu: 1500,
237 ipv4: Some(MsbnetReadyIpv4 {
238 address: "100.96.0.2".to_string(),
239 prefix_len: 30,
240 gateway: "100.96.0.1".to_string(),
241 dns: vec![],
242 }),
243 ipv6: None,
244 tls: None,
245 };
246
247 let vars = ready.to_env_vars();
248 assert_eq!(vars.len(), 2);
249 assert_eq!(vars[1].0, "MSB_NET_IPV4");
250 assert_eq!(vars[1].1, "addr=100.96.0.2/30,gw=100.96.0.1");
251 }
252}