sandbox_rs/network/
config.rs

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