sandbox_rs/network/
config.rs

1//! Network configuration for sandbox isolation
2
3use crate::errors::{Result, SandboxError};
4use serde::{Deserialize, Serialize};
5use std::net::{IpAddr, Ipv4Addr, SocketAddr};
6
7/// Network interface configuration
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct NetworkInterface {
10    /// Interface name
11    pub name: String,
12    /// IPv4 address
13    pub ipv4: Ipv4Addr,
14    /// Netmask
15    pub netmask: Ipv4Addr,
16    /// Gateway
17    pub gateway: Option<Ipv4Addr>,
18    /// Whether to enable
19    pub enabled: bool,
20}
21
22impl Default for NetworkInterface {
23    fn default() -> Self {
24        Self {
25            name: "eth0".to_string(),
26            ipv4: Ipv4Addr::new(172, 17, 0, 2),
27            netmask: Ipv4Addr::new(255, 255, 255, 0),
28            gateway: Some(Ipv4Addr::new(172, 17, 0, 1)),
29            enabled: true,
30        }
31    }
32}
33
34impl NetworkInterface {
35    /// Create new network interface
36    pub fn new(name: &str, ipv4: Ipv4Addr) -> Self {
37        Self {
38            name: name.to_string(),
39            ipv4,
40            netmask: Ipv4Addr::new(255, 255, 255, 0),
41            gateway: Some(Ipv4Addr::new(172, 17, 0, 1)),
42            enabled: true,
43        }
44    }
45
46    /// Validate interface configuration
47    pub fn validate(&self) -> Result<()> {
48        if self.name.is_empty() {
49            return Err(SandboxError::InvalidConfig(
50                "Interface name cannot be empty".to_string(),
51            ));
52        }
53
54        // Check if IP is valid container range (not 0.0.0.0 or broadcast)
55        if self.ipv4.is_unspecified() || self.ipv4.is_broadcast() {
56            return Err(SandboxError::InvalidConfig(
57                "Invalid IP address for interface".to_string(),
58            ));
59        }
60
61        Ok(())
62    }
63
64    /// Get CIDR notation
65    pub fn get_cidr(&self) -> String {
66        format!("{}/{}", self.ipv4, self.netmask_bits())
67    }
68
69    /// Get netmask bits (/24, /16, etc.)
70    pub fn netmask_bits(&self) -> u8 {
71        let octets = self.netmask.octets();
72        let mut bits = 0u8;
73
74        for octet in octets {
75            bits += octet.count_ones() as u8;
76        }
77
78        bits
79    }
80}
81
82/// Network mode for sandbox
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
84pub enum NetworkMode {
85    /// Isolated network namespace
86    #[default]
87    Isolated,
88    /// Bridge mode (connected via virtual bridge)
89    Bridge,
90    /// Host network namespace
91    Host,
92    /// Custom configuration
93    Custom,
94}
95
96impl std::fmt::Display for NetworkMode {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        match self {
99            NetworkMode::Isolated => write!(f, "isolated"),
100            NetworkMode::Bridge => write!(f, "bridge"),
101            NetworkMode::Host => write!(f, "host"),
102            NetworkMode::Custom => write!(f, "custom"),
103        }
104    }
105}
106
107/// Network configuration
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct NetworkConfig {
110    /// Network mode
111    pub mode: NetworkMode,
112    /// Network interfaces
113    pub interfaces: Vec<NetworkInterface>,
114    /// DNS servers
115    pub dns_servers: Vec<IpAddr>,
116    /// Exposed ports (container:host)
117    pub port_mappings: Vec<PortMapping>,
118    /// Enable IP forwarding
119    pub ip_forward: bool,
120    /// Maximum bandwidth (bytes/sec, 0 = unlimited)
121    pub bandwidth_limit: u64,
122}
123
124impl Default for NetworkConfig {
125    fn default() -> Self {
126        Self {
127            mode: NetworkMode::Isolated,
128            interfaces: vec![NetworkInterface::default()],
129            dns_servers: vec![
130                IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
131                IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
132            ],
133            port_mappings: Vec::new(),
134            ip_forward: false,
135            bandwidth_limit: 0,
136        }
137    }
138}
139
140impl NetworkConfig {
141    /// Create isolated network config
142    pub fn isolated() -> Self {
143        Self {
144            mode: NetworkMode::Isolated,
145            interfaces: vec![NetworkInterface::default()],
146            dns_servers: vec![IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))],
147            port_mappings: Vec::new(),
148            ip_forward: false,
149            bandwidth_limit: 0,
150        }
151    }
152
153    /// Create host network config
154    pub fn host() -> Self {
155        Self {
156            mode: NetworkMode::Host,
157            interfaces: Vec::new(),
158            dns_servers: Vec::new(),
159            port_mappings: Vec::new(),
160            ip_forward: true,
161            bandwidth_limit: 0,
162        }
163    }
164
165    /// Add interface
166    pub fn add_interface(&mut self, iface: NetworkInterface) -> Result<()> {
167        iface.validate()?;
168        self.interfaces.push(iface);
169        Ok(())
170    }
171
172    /// Add port mapping
173    pub fn add_port_mapping(&mut self, mapping: PortMapping) -> Result<()> {
174        mapping.validate()?;
175        self.port_mappings.push(mapping);
176        Ok(())
177    }
178
179    /// Validate configuration
180    pub fn validate(&self) -> Result<()> {
181        for iface in &self.interfaces {
182            iface.validate()?;
183        }
184
185        for mapping in &self.port_mappings {
186            mapping.validate()?;
187        }
188
189        Ok(())
190    }
191}
192
193/// Port mapping configuration
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct PortMapping {
196    /// Container port
197    pub container_port: u16,
198    /// Host port
199    pub host_port: u16,
200    /// Protocol (tcp/udp)
201    pub protocol: String,
202}
203
204impl PortMapping {
205    /// Create new port mapping
206    pub fn new(container_port: u16, host_port: u16) -> Self {
207        Self {
208            container_port,
209            host_port,
210            protocol: "tcp".to_string(),
211        }
212    }
213
214    /// Validate port mapping
215    pub fn validate(&self) -> Result<()> {
216        if self.container_port == 0 || self.host_port == 0 {
217            return Err(SandboxError::InvalidConfig(
218                "Port numbers must be > 0".to_string(),
219            ));
220        }
221
222        if !["tcp", "udp"].contains(&self.protocol.as_str()) {
223            return Err(SandboxError::InvalidConfig(
224                "Protocol must be tcp or udp".to_string(),
225            ));
226        }
227
228        Ok(())
229    }
230
231    /// Get socket address for host
232    pub fn get_host_addr(&self) -> SocketAddr {
233        SocketAddr::from((Ipv4Addr::LOCALHOST, self.host_port))
234    }
235
236    /// Get socket address for container
237    pub fn get_container_addr(&self, ip: Ipv4Addr) -> SocketAddr {
238        SocketAddr::from((ip, self.container_port))
239    }
240}
241
242/// Network statistics
243#[derive(Debug, Clone, Default, Serialize, Deserialize)]
244pub struct NetworkStats {
245    /// Bytes received
246    pub bytes_recv: u64,
247    /// Bytes sent
248    pub bytes_sent: u64,
249    /// Packets received
250    pub packets_recv: u64,
251    /// Packets sent
252    pub packets_sent: u64,
253    /// Errors
254    pub errors: u64,
255    /// Dropped packets
256    pub dropped: u64,
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_network_interface_creation() {
265        let iface = NetworkInterface::new("eth0", Ipv4Addr::new(192, 168, 1, 10));
266        assert_eq!(iface.name, "eth0");
267        assert_eq!(iface.ipv4, Ipv4Addr::new(192, 168, 1, 10));
268    }
269
270    #[test]
271    fn test_network_interface_validation() {
272        let mut iface = NetworkInterface::default();
273        assert!(iface.validate().is_ok());
274
275        iface.ipv4 = Ipv4Addr::UNSPECIFIED;
276        assert!(iface.validate().is_err());
277    }
278
279    #[test]
280    fn test_network_interface_cidr() {
281        let iface = NetworkInterface::default();
282        let cidr = iface.get_cidr();
283        assert!(cidr.contains("/"));
284    }
285
286    #[test]
287    fn test_netmask_bits() {
288        let iface = NetworkInterface {
289            netmask: Ipv4Addr::new(255, 255, 255, 0),
290            ..Default::default()
291        };
292        assert_eq!(iface.netmask_bits(), 24);
293    }
294
295    #[test]
296    fn test_network_mode_display() {
297        assert_eq!(NetworkMode::Isolated.to_string(), "isolated");
298        assert_eq!(NetworkMode::Bridge.to_string(), "bridge");
299        assert_eq!(NetworkMode::Host.to_string(), "host");
300    }
301
302    #[test]
303    fn test_network_config_default() {
304        let config = NetworkConfig::default();
305        assert_eq!(config.mode, NetworkMode::Isolated);
306        assert!(!config.interfaces.is_empty());
307    }
308
309    #[test]
310    fn test_network_config_isolated() {
311        let config = NetworkConfig::isolated();
312        assert_eq!(config.mode, NetworkMode::Isolated);
313    }
314
315    #[test]
316    fn test_network_config_host() {
317        let config = NetworkConfig::host();
318        assert_eq!(config.mode, NetworkMode::Host);
319        assert!(config.ip_forward);
320    }
321
322    #[test]
323    fn test_port_mapping_creation() {
324        let mapping = PortMapping::new(8080, 8080);
325        assert_eq!(mapping.container_port, 8080);
326        assert_eq!(mapping.host_port, 8080);
327    }
328
329    #[test]
330    fn test_port_mapping_validation() {
331        let mapping = PortMapping::new(8080, 8080);
332        assert!(mapping.validate().is_ok());
333
334        let bad_mapping = PortMapping {
335            container_port: 0,
336            host_port: 8080,
337            protocol: "tcp".to_string(),
338        };
339        assert!(bad_mapping.validate().is_err());
340    }
341
342    #[test]
343    fn test_port_mapping_addresses() {
344        let mapping = PortMapping::new(8080, 8080);
345        let host_addr = mapping.get_host_addr();
346        assert_eq!(host_addr.port(), 8080);
347    }
348
349    #[test]
350    fn test_network_stats_default() {
351        let stats = NetworkStats::default();
352        assert_eq!(stats.bytes_recv, 0);
353        assert_eq!(stats.bytes_sent, 0);
354    }
355}