wireguard_uapi/
get.rs

1use derive_builder::Builder;
2use std::net::{IpAddr, SocketAddr};
3use std::str::FromStr;
4use std::time::Duration;
5
6#[derive(Builder, Debug, PartialEq, Eq)]
7pub struct Device {
8    pub ifindex: u32,
9    pub ifname: String,
10    #[builder(default)]
11    pub private_key: Option<[u8; 32]>,
12    #[builder(default)]
13    pub public_key: Option<[u8; 32]>,
14    pub listen_port: u16,
15    pub fwmark: u32,
16    #[builder(default)]
17    pub peers: Vec<Peer>,
18}
19
20#[derive(Builder, Clone, Debug, PartialEq, Eq)]
21pub struct Peer {
22    // The public_key and allowed_ips fields are public to
23    // make peer coalescing easier.
24    #[builder(field(public))]
25    pub public_key: [u8; 32],
26    pub preshared_key: [u8; 32],
27    #[builder(default)]
28    pub endpoint: Option<SocketAddr>,
29    pub persistent_keepalive_interval: u16,
30    pub last_handshake_time: Duration,
31    pub rx_bytes: u64,
32    pub tx_bytes: u64,
33    #[builder(default, field(public))]
34    pub allowed_ips: Vec<AllowedIp>,
35    pub protocol_version: u32,
36}
37
38#[derive(Builder, Clone, Debug, PartialEq, Eq)]
39pub struct AllowedIp {
40    pub family: u16,
41    pub ipaddr: IpAddr,
42    pub cidr_mask: u8,
43}
44
45#[derive(Debug, thiserror::Error, PartialEq, Eq)]
46pub enum ParseAllowedIpError {
47    #[error("String is missing CIDR mask: `${0}`")]
48    MissingCidrMask(String),
49    #[error(transparent)]
50    AddrParseError(#[from] std::net::AddrParseError),
51    #[error(transparent)]
52    InvalidCidrMask(#[from] std::num::ParseIntError),
53}
54
55impl FromStr for AllowedIp {
56    type Err = ParseAllowedIpError;
57
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        let mut tokens = s.splitn(2, '/');
60
61        // The unwrap should always succeed since there's at least token.
62        let ipaddr = tokens.next().unwrap().parse()?;
63        let cidr_mask = tokens
64            .next()
65            .filter(|s| !s.is_empty())
66            .ok_or_else(|| Self::Err::MissingCidrMask(s.to_string()))?
67            .parse()?;
68
69        Ok(AllowedIp {
70            family: match ipaddr {
71                // This code should compile on non-nix systems, so we can't use
72                // libc constants directly here.
73                IpAddr::V4(_) => 2,  // libc::AF_INET
74                IpAddr::V6(_) => 10, // libc::AF_INET6
75            },
76            ipaddr,
77            cidr_mask,
78        })
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use std::net::Ipv4Addr;
86    use std::net::Ipv6Addr;
87
88    #[test]
89    fn parse_invalid_allowed_ip() {
90        assert!(matches!(
91            "".parse::<AllowedIp>(),
92            Err(ParseAllowedIpError::AddrParseError(_))
93        ));
94
95        assert!(matches!(
96            "10.24.24.3".parse::<AllowedIp>(),
97            Err(ParseAllowedIpError::MissingCidrMask(_))
98        ));
99
100        assert!(matches!(
101            "10.24.24.3/".parse::<AllowedIp>(),
102            Err(ParseAllowedIpError::MissingCidrMask(_))
103        ));
104
105        assert!(matches!(
106            "10.24.24.3/a".parse::<AllowedIp>(),
107            Err(ParseAllowedIpError::InvalidCidrMask(_))
108        ));
109    }
110
111    #[test]
112    fn parse_allowed_ip_ipv4() {
113        let actual = "10.24.24.3/32".parse();
114        let expected = Ok(AllowedIp {
115            family: 2,
116            ipaddr: IpAddr::V4(Ipv4Addr::new(10, 24, 24, 3)),
117            cidr_mask: 32,
118        });
119        assert_eq!(actual, expected);
120    }
121
122    #[test]
123    fn parse_allowed_ip_ipv6() {
124        let actual = "::1/128".parse();
125        let expected = Ok(AllowedIp {
126            family: 10,
127            ipaddr: IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
128            cidr_mask: 128,
129        });
130        assert_eq!(actual, expected);
131    }
132}