corevpn_core/
network.rs

1//! Network types and IP address management
2
3use ipnet::{IpNet, Ipv4Net, Ipv6Net};
4use serde::{Deserialize, Serialize};
5use std::collections::HashSet;
6use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7
8use crate::{CoreError, Result};
9
10/// VPN IP address assigned to a client
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct VpnAddress {
13    /// IPv4 address (if assigned)
14    pub ipv4: Option<Ipv4Addr>,
15    /// IPv6 address (if assigned)
16    pub ipv6: Option<Ipv6Addr>,
17}
18
19impl VpnAddress {
20    /// Create with only IPv4
21    pub fn v4(addr: Ipv4Addr) -> Self {
22        Self {
23            ipv4: Some(addr),
24            ipv6: None,
25        }
26    }
27
28    /// Create with only IPv6
29    pub fn v6(addr: Ipv6Addr) -> Self {
30        Self {
31            ipv4: None,
32            ipv6: Some(addr),
33        }
34    }
35
36    /// Create with both IPv4 and IPv6
37    pub fn dual(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
38        Self {
39            ipv4: Some(ipv4),
40            ipv6: Some(ipv6),
41        }
42    }
43
44    /// Get primary address (prefers IPv4)
45    pub fn primary(&self) -> Option<IpAddr> {
46        self.ipv4
47            .map(IpAddr::V4)
48            .or_else(|| self.ipv6.map(IpAddr::V6))
49    }
50}
51
52/// Route to be pushed to VPN clients
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54pub struct Route {
55    /// Network/prefix to route
56    pub network: IpNet,
57    /// Gateway (None = use VPN gateway)
58    pub gateway: Option<IpAddr>,
59    /// Metric/priority
60    pub metric: u32,
61}
62
63impl Route {
64    /// Create a new route
65    pub fn new(network: IpNet) -> Self {
66        Self {
67            network,
68            gateway: None,
69            metric: 0,
70        }
71    }
72
73    /// Create default route (0.0.0.0/0)
74    pub fn default_v4() -> Self {
75        Self {
76            network: "0.0.0.0/0".parse().unwrap(),
77            gateway: None,
78            metric: 0,
79        }
80    }
81
82    /// Create default IPv6 route (::/0)
83    pub fn default_v6() -> Self {
84        Self {
85            network: "::/0".parse().unwrap(),
86            gateway: None,
87            metric: 0,
88        }
89    }
90
91    /// Set gateway
92    pub fn with_gateway(mut self, gateway: IpAddr) -> Self {
93        self.gateway = Some(gateway);
94        self
95    }
96
97    /// Set metric
98    pub fn with_metric(mut self, metric: u32) -> Self {
99        self.metric = metric;
100        self
101    }
102}
103
104/// IP address pool for assigning addresses to VPN clients
105pub struct AddressPool {
106    /// IPv4 network range
107    ipv4_net: Option<Ipv4Net>,
108    /// IPv6 network range
109    ipv6_net: Option<Ipv6Net>,
110    /// Allocated IPv4 addresses
111    allocated_v4: parking_lot::RwLock<HashSet<Ipv4Addr>>,
112    /// Allocated IPv6 addresses
113    allocated_v6: parking_lot::RwLock<HashSet<Ipv6Addr>>,
114    /// Reserved addresses (e.g., gateway, broadcast)
115    reserved_v4: HashSet<Ipv4Addr>,
116    /// Reserved IPv6 addresses
117    reserved_v6: HashSet<Ipv6Addr>,
118}
119
120impl AddressPool {
121    /// Create a new address pool
122    ///
123    /// # Arguments
124    /// * `ipv4_net` - IPv4 network (e.g., "10.8.0.0/24")
125    /// * `ipv6_net` - IPv6 network (e.g., "fd00::/64")
126    pub fn new(ipv4_net: Option<Ipv4Net>, ipv6_net: Option<Ipv6Net>) -> Self {
127        let mut reserved_v4 = HashSet::new();
128        let mut reserved_v6 = HashSet::new();
129
130        // Reserve network and broadcast addresses for IPv4
131        if let Some(net) = &ipv4_net {
132            reserved_v4.insert(net.network());
133            reserved_v4.insert(net.broadcast());
134            // Reserve .1 for gateway
135            let gateway = Ipv4Addr::from(u32::from(net.network()) + 1);
136            reserved_v4.insert(gateway);
137        }
138
139        // Reserve first address for IPv6 gateway
140        if let Some(net) = &ipv6_net {
141            let gateway = Ipv6Addr::from(u128::from(net.network()) + 1);
142            reserved_v6.insert(gateway);
143        }
144
145        Self {
146            ipv4_net,
147            ipv6_net,
148            allocated_v4: parking_lot::RwLock::new(HashSet::new()),
149            allocated_v6: parking_lot::RwLock::new(HashSet::new()),
150            reserved_v4,
151            reserved_v6,
152        }
153    }
154
155    /// Get the gateway IPv4 address
156    pub fn gateway_v4(&self) -> Option<Ipv4Addr> {
157        self.ipv4_net.map(|net| {
158            Ipv4Addr::from(u32::from(net.network()) + 1)
159        })
160    }
161
162    /// Get the gateway IPv6 address
163    pub fn gateway_v6(&self) -> Option<Ipv6Addr> {
164        self.ipv6_net.map(|net| {
165            Ipv6Addr::from(u128::from(net.network()) + 1)
166        })
167    }
168
169    /// Allocate an address from the pool
170    pub fn allocate(&self) -> Result<VpnAddress> {
171        let ipv4 = if let Some(net) = &self.ipv4_net {
172            Some(self.allocate_v4(net)?)
173        } else {
174            None
175        };
176
177        let ipv6 = if let Some(net) = &self.ipv6_net {
178            Some(self.allocate_v6(net)?)
179        } else {
180            None
181        };
182
183        if ipv4.is_none() && ipv6.is_none() {
184            return Err(CoreError::ConfigError("No address pools configured".into()));
185        }
186
187        Ok(VpnAddress { ipv4, ipv6 })
188    }
189
190    fn allocate_v4(&self, net: &Ipv4Net) -> Result<Ipv4Addr> {
191        let mut allocated = self.allocated_v4.write();
192
193        // Start from .2 (after gateway)
194        let start = u32::from(net.network()) + 2;
195        let end = u32::from(net.broadcast());
196
197        for addr_u32 in start..end {
198            let addr = Ipv4Addr::from(addr_u32);
199            if !self.reserved_v4.contains(&addr) && !allocated.contains(&addr) {
200                allocated.insert(addr);
201                return Ok(addr);
202            }
203        }
204
205        Err(CoreError::AddressPoolExhausted)
206    }
207
208    fn allocate_v6(&self, net: &Ipv6Net) -> Result<Ipv6Addr> {
209        let mut allocated = self.allocated_v6.write();
210
211        // Start from ::2 (after gateway)
212        let start = u128::from(net.network()) + 2;
213        // Limit search to reasonable range
214        let end = start + 65534;
215
216        for addr_u128 in start..end {
217            let addr = Ipv6Addr::from(addr_u128);
218            if !self.reserved_v6.contains(&addr) && !allocated.contains(&addr) {
219                allocated.insert(addr);
220                return Ok(addr);
221            }
222        }
223
224        Err(CoreError::AddressPoolExhausted)
225    }
226
227    /// Allocate a specific address (for static assignment)
228    pub fn allocate_specific(&self, addr: VpnAddress) -> Result<VpnAddress> {
229        if let Some(v4) = addr.ipv4 {
230            let mut allocated = self.allocated_v4.write();
231            if let Some(net) = &self.ipv4_net {
232                if !net.contains(&v4) {
233                    return Err(CoreError::InvalidAddress(format!(
234                        "{} not in pool {}",
235                        v4, net
236                    )));
237                }
238            }
239            if self.reserved_v4.contains(&v4) || allocated.contains(&v4) {
240                return Err(CoreError::InvalidAddress(format!(
241                    "{} is reserved or already allocated",
242                    v4
243                )));
244            }
245            allocated.insert(v4);
246        }
247
248        if let Some(v6) = addr.ipv6 {
249            let mut allocated = self.allocated_v6.write();
250            if let Some(net) = &self.ipv6_net {
251                if !net.contains(&v6) {
252                    return Err(CoreError::InvalidAddress(format!(
253                        "{} not in pool {}",
254                        v6, net
255                    )));
256                }
257            }
258            if self.reserved_v6.contains(&v6) || allocated.contains(&v6) {
259                return Err(CoreError::InvalidAddress(format!(
260                    "{} is reserved or already allocated",
261                    v6
262                )));
263            }
264            allocated.insert(v6);
265        }
266
267        Ok(addr)
268    }
269
270    /// Release an address back to the pool
271    pub fn release(&self, addr: &VpnAddress) {
272        if let Some(v4) = addr.ipv4 {
273            self.allocated_v4.write().remove(&v4);
274        }
275        if let Some(v6) = addr.ipv6 {
276            self.allocated_v6.write().remove(&v6);
277        }
278    }
279
280    /// Get the number of available IPv4 addresses
281    pub fn available_v4(&self) -> usize {
282        if let Some(net) = &self.ipv4_net {
283            let total = net.hosts().count();
284            let reserved = self.reserved_v4.len();
285            let allocated = self.allocated_v4.read().len();
286            total.saturating_sub(reserved).saturating_sub(allocated)
287        } else {
288            0
289        }
290    }
291
292    /// Get the number of available IPv6 addresses
293    pub fn available_v6(&self) -> usize {
294        if self.ipv6_net.is_some() {
295            // IPv6 pools are effectively unlimited, return large number
296            let allocated = self.allocated_v6.read().len();
297            65534usize.saturating_sub(allocated)
298        } else {
299            0
300        }
301    }
302
303    /// Get pool statistics
304    pub fn stats(&self) -> PoolStats {
305        PoolStats {
306            ipv4_total: self.ipv4_net.map(|n| n.hosts().count()).unwrap_or(0),
307            ipv4_allocated: self.allocated_v4.read().len(),
308            ipv4_available: self.available_v4(),
309            ipv6_allocated: self.allocated_v6.read().len(),
310            ipv6_available: self.available_v6(),
311        }
312    }
313}
314
315/// Address pool statistics
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct PoolStats {
318    /// Total IPv4 addresses in pool
319    pub ipv4_total: usize,
320    /// Allocated IPv4 addresses
321    pub ipv4_allocated: usize,
322    /// Available IPv4 addresses
323    pub ipv4_available: usize,
324    /// Allocated IPv6 addresses
325    pub ipv6_allocated: usize,
326    /// Available IPv6 addresses
327    pub ipv6_available: usize,
328}
329
330/// DNS configuration to push to clients
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct DnsConfig {
333    /// DNS servers
334    pub servers: Vec<IpAddr>,
335    /// Search domains
336    pub search_domains: Vec<String>,
337}
338
339impl Default for DnsConfig {
340    fn default() -> Self {
341        Self {
342            servers: vec![
343                "1.1.1.1".parse().unwrap(),  // Cloudflare
344                "1.0.0.1".parse().unwrap(),
345            ],
346            search_domains: vec![],
347        }
348    }
349}
350
351#[cfg(test)]
352mod tests {
353    use super::*;
354
355    #[test]
356    fn test_address_pool() {
357        let pool = AddressPool::new(
358            Some("10.8.0.0/24".parse().unwrap()),
359            None,
360        );
361
362        // First allocation should be .2
363        let addr1 = pool.allocate().unwrap();
364        assert_eq!(addr1.ipv4, Some("10.8.0.2".parse().unwrap()));
365
366        // Second should be .3
367        let addr2 = pool.allocate().unwrap();
368        assert_eq!(addr2.ipv4, Some("10.8.0.3".parse().unwrap()));
369
370        // Release first
371        pool.release(&addr1);
372
373        // Next allocation should reuse .2
374        let addr3 = pool.allocate().unwrap();
375        assert_eq!(addr3.ipv4, Some("10.8.0.2".parse().unwrap()));
376    }
377
378    #[test]
379    fn test_gateway_address() {
380        let pool = AddressPool::new(
381            Some("10.8.0.0/24".parse().unwrap()),
382            Some("fd00::/64".parse().unwrap()),
383        );
384
385        assert_eq!(pool.gateway_v4(), Some("10.8.0.1".parse().unwrap()));
386        assert_eq!(pool.gateway_v6(), Some("fd00::1".parse().unwrap()));
387    }
388
389    #[test]
390    fn test_route() {
391        let route = Route::new("192.168.1.0/24".parse().unwrap())
392            .with_gateway("10.8.0.1".parse().unwrap())
393            .with_metric(100);
394
395        assert_eq!(route.metric, 100);
396        assert_eq!(route.gateway, Some("10.8.0.1".parse().unwrap()));
397    }
398}