Skip to main content

aivpn_common/
network_config.rs

1use std::net::Ipv4Addr;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::{Error, Result};
6
7pub const DEFAULT_VPN_MTU: u16 = 1346;
8pub const LEGACY_VPN_PREFIX_LEN: u8 = 24;
9pub const LEGACY_SERVER_VPN_IP: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 1);
10
11fn default_server_vpn_ip() -> Ipv4Addr {
12    LEGACY_SERVER_VPN_IP
13}
14
15fn default_prefix_len() -> u8 {
16    LEGACY_VPN_PREFIX_LEN
17}
18
19fn default_mtu() -> u16 {
20    DEFAULT_VPN_MTU
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24pub struct VpnNetworkConfig {
25    #[serde(default = "default_server_vpn_ip")]
26    pub server_vpn_ip: Ipv4Addr,
27    #[serde(default = "default_prefix_len")]
28    pub prefix_len: u8,
29    #[serde(default = "default_mtu")]
30    pub mtu: u16,
31}
32
33impl Default for VpnNetworkConfig {
34    fn default() -> Self {
35        Self {
36            server_vpn_ip: default_server_vpn_ip(),
37            prefix_len: default_prefix_len(),
38            mtu: default_mtu(),
39        }
40    }
41}
42
43impl VpnNetworkConfig {
44    pub fn validate(&self) -> Result<()> {
45        if !(1..=30).contains(&self.prefix_len) {
46            return Err(Error::InvalidPacket(
47                "VPN prefix length must be in range 1..=30",
48            ));
49        }
50        if self.server_vpn_ip == self.network_addr() || self.server_vpn_ip == self.broadcast_addr()
51        {
52            return Err(Error::InvalidPacket(
53                "Server VPN IP must be a usable host address",
54            ));
55        }
56        Ok(())
57    }
58
59    pub fn netmask(&self) -> Ipv4Addr {
60        prefix_len_to_netmask(self.prefix_len)
61    }
62
63    pub fn network_addr(&self) -> Ipv4Addr {
64        Ipv4Addr::from(ipv4_to_u32(self.server_vpn_ip) & self.mask_u32())
65    }
66
67    pub fn broadcast_addr(&self) -> Ipv4Addr {
68        Ipv4Addr::from(self.network_u32() | !self.mask_u32())
69    }
70
71    pub fn contains(&self, ip: Ipv4Addr) -> bool {
72        (ipv4_to_u32(ip) & self.mask_u32()) == self.network_u32()
73    }
74
75    pub fn cidr_string(&self) -> String {
76        format!("{}/{}", self.network_addr(), self.prefix_len)
77    }
78
79    pub fn server_ip_string(&self) -> String {
80        self.server_vpn_ip.to_string()
81    }
82
83    pub fn netmask_string(&self) -> String {
84        self.netmask().to_string()
85    }
86
87    pub fn host_offset(&self, ip: Ipv4Addr) -> u32 {
88        ipv4_to_u32(ip) & !self.mask_u32()
89    }
90
91    pub fn max_host_offset(&self) -> u32 {
92        let host_mask = !self.mask_u32();
93        host_mask.saturating_sub(1)
94    }
95
96    pub fn is_usable_host(&self, ip: Ipv4Addr) -> bool {
97        self.contains(ip) && ip != self.network_addr() && ip != self.broadcast_addr()
98    }
99
100    pub fn ip_for_host_offset(&self, host_offset: u32) -> Option<Ipv4Addr> {
101        if host_offset == 0 || host_offset > self.max_host_offset() {
102            return None;
103        }
104        Some(Ipv4Addr::from(self.network_u32() | host_offset))
105    }
106
107    pub fn client_config(&self, client_ip: Ipv4Addr) -> Result<ClientNetworkConfig> {
108        if !self.is_usable_host(client_ip) {
109            return Err(Error::InvalidPacket(
110                "Client VPN IP is outside configured VPN subnet",
111            ));
112        }
113        if client_ip == self.server_vpn_ip {
114            return Err(Error::InvalidPacket(
115                "Client VPN IP cannot equal server VPN IP",
116            ));
117        }
118        Ok(ClientNetworkConfig {
119            client_ip,
120            server_vpn_ip: self.server_vpn_ip,
121            prefix_len: self.prefix_len,
122            mtu: self.mtu,
123            mdh_len: default_mdh_len(),
124        })
125    }
126
127    fn network_u32(&self) -> u32 {
128        ipv4_to_u32(self.server_vpn_ip) & self.mask_u32()
129    }
130
131    fn mask_u32(&self) -> u32 {
132        if self.prefix_len == 0 {
133            0
134        } else {
135            u32::MAX << (32 - self.prefix_len)
136        }
137    }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
141pub struct ClientNetworkConfig {
142    pub client_ip: Ipv4Addr,
143    pub server_vpn_ip: Ipv4Addr,
144    pub prefix_len: u8,
145    #[serde(default = "default_mtu")]
146    pub mtu: u16,
147    /// Mask-dependent header length in bytes.
148    /// Clients MUST use this value for MDH generation and parsing.
149    /// Defaults to 20 (STUN/WebRTC mask) for backward compatibility.
150    #[serde(default = "default_mdh_len")]
151    pub mdh_len: u16,
152}
153
154fn default_mdh_len() -> u16 {
155    20
156}
157
158impl ClientNetworkConfig {
159    pub const WIRE_SIZE: usize = 12;
160    const WIRE_VERSION: u8 = 1;
161
162    pub fn validate(&self) -> Result<()> {
163        VpnNetworkConfig {
164            server_vpn_ip: self.server_vpn_ip,
165            prefix_len: self.prefix_len,
166            mtu: self.mtu,
167        }
168        .client_config(self.client_ip)
169        .map(|_| ())
170    }
171
172    pub fn netmask(&self) -> Ipv4Addr {
173        prefix_len_to_netmask(self.prefix_len)
174    }
175
176    pub fn cidr_string(&self) -> String {
177        format!("{}/{}", self.client_ip, self.prefix_len)
178    }
179
180    pub fn netmask_string(&self) -> String {
181        self.netmask().to_string()
182    }
183
184    pub fn encode_wire(&self) -> [u8; Self::WIRE_SIZE] {
185        let mut buf = [0u8; Self::WIRE_SIZE];
186        buf[0] = Self::WIRE_VERSION;
187        buf[1] = self.prefix_len;
188        buf[2..4].copy_from_slice(&self.mtu.to_le_bytes());
189        buf[4..8].copy_from_slice(&self.server_vpn_ip.octets());
190        buf[8..12].copy_from_slice(&self.client_ip.octets());
191        buf
192    }
193
194    pub fn decode_wire(data: &[u8]) -> Result<Self> {
195        if data.len() != Self::WIRE_SIZE {
196            return Err(Error::InvalidPacket(
197                "Client network config has invalid wire length",
198            ));
199        }
200        if data[0] != Self::WIRE_VERSION {
201            return Err(Error::InvalidPacket(
202                "Unsupported client network config wire version",
203            ));
204        }
205
206        let config = Self {
207            prefix_len: data[1],
208            mtu: u16::from_le_bytes([data[2], data[3]]),
209            server_vpn_ip: Ipv4Addr::new(data[4], data[5], data[6], data[7]),
210            client_ip: Ipv4Addr::new(data[8], data[9], data[10], data[11]),
211            mdh_len: default_mdh_len(),
212        };
213        config.validate()?;
214        Ok(config)
215    }
216}
217
218pub fn prefix_len_to_netmask(prefix_len: u8) -> Ipv4Addr {
219    if prefix_len == 0 {
220        return Ipv4Addr::new(0, 0, 0, 0);
221    }
222
223    Ipv4Addr::from(u32::MAX << (32 - prefix_len))
224}
225
226pub fn netmask_to_prefix_len(netmask: Ipv4Addr) -> Result<u8> {
227    let mask = ipv4_to_u32(netmask);
228    let prefix_len = mask.leading_ones() as u8;
229    let expected = if prefix_len == 0 {
230        0
231    } else {
232        u32::MAX << (32 - prefix_len)
233    };
234    if mask != expected {
235        return Err(Error::InvalidPacket("VPN netmask must be contiguous"));
236    }
237    Ok(prefix_len)
238}
239
240fn ipv4_to_u32(ip: Ipv4Addr) -> u32 {
241    u32::from(ip)
242}
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    #[test]
249    fn wire_roundtrip_preserves_client_network_config() {
250        let config = ClientNetworkConfig {
251            client_ip: Ipv4Addr::new(10, 150, 0, 2),
252            server_vpn_ip: Ipv4Addr::new(10, 150, 0, 1),
253            prefix_len: 24,
254            mtu: 1346,
255            mdh_len: 20,
256        };
257
258        let decoded = ClientNetworkConfig::decode_wire(&config.encode_wire()).unwrap();
259        assert_eq!(decoded, config);
260    }
261
262    #[test]
263    fn network_helpers_compute_addresses() {
264        let config = VpnNetworkConfig {
265            server_vpn_ip: Ipv4Addr::new(10, 150, 0, 1),
266            prefix_len: 24,
267            mtu: 1346,
268        };
269
270        assert_eq!(config.network_addr(), Ipv4Addr::new(10, 150, 0, 0));
271        assert_eq!(config.broadcast_addr(), Ipv4Addr::new(10, 150, 0, 255));
272        assert_eq!(config.netmask(), Ipv4Addr::new(255, 255, 255, 0));
273        assert!(config.is_usable_host(Ipv4Addr::new(10, 150, 0, 10)));
274        assert!(!config.is_usable_host(Ipv4Addr::new(10, 150, 0, 0)));
275    }
276}