wireguard_conf/models/
interface.rs

1use either::Either;
2use ipnet::Ipv4Net;
3
4use std::fmt;
5
6use crate::prelude::*;
7
8/// Controls the routing table to which routes are added.
9#[derive(PartialEq, Eq, Clone, Debug, Default)]
10pub enum Table {
11    /// Routing table
12    RoutingTable(usize),
13
14    /// Disables the creation of routes altogether
15    Off,
16
17    /// Adds routes to the default table and enables special handling of default routes.
18    #[default]
19    Auto,
20}
21
22impl fmt::Display for Table {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            Table::RoutingTable(n) => write!(f, "{n}"),
26            Table::Off => write!(f, "off"),
27            Table::Auto => write!(f, "auto"),
28        }
29    }
30}
31
32/// Struct, that represents complete configuration (contains both `[Interface]` and `[Peer]`
33/// sections).
34///
35/// Use [`InterfaceBuilder`] to create interface.
36///
37/// [Wireguard docs](https://github.com/pirate/wireguard-docs#interface)
38#[must_use]
39#[derive(Clone, Debug)]
40pub struct Interface {
41    /// Interface's address.
42    ///
43    /// [Wireguard docs](https://github.com/pirate/wireguard-docs#address)
44    pub address: Ipv4Net,
45
46    /// Port to listen for incoming VPN connections.
47    ///
48    /// [Wireguard conf](https://github.com/pirate/wireguard-docs#listenport)
49    pub listen_port: Option<u16>,
50
51    /// Node's private key.
52    ///
53    /// [Wireguard conf](https://github.com/pirate/wireguard-docs#privatekey)
54    pub private_key: PrivateKey,
55
56    /// The DNS servers to announce to VPN clients via DHCP.
57    ///
58    /// [Wireguard docs](https://github.com/pirate/wireguard-docs#dns-2)
59    pub dns: Vec<String>,
60
61    /// Endpoint.
62    ///
63    /// - `[Interface]` section will have `# Name = <endpoint>` comment at the top.
64    /// - Exported [`Peer`] (via [`Interface::to_peer`]) will have this endpoint.
65    ///
66    /// [Wireguard Docs for `# Name`](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#-name-1);
67    /// [Wireguard Docs for endpoint](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#endpoint)
68    pub endpoint: Option<String>,
69
70    /// Routing table to use for the WireGuard routes.
71    ///
72    /// See [`Table`] for special values.
73    ///
74    /// [Wireguard docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#table)
75    pub table: Option<Table>,
76
77    /// Maximum Transmission Unit (MTU, aka packet/frame size) to use when connecting to the peer.
78    ///
79    /// [Wireguard docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#mtu)
80    pub mtu: Option<usize>,
81
82    /// AmneziaWG obfuscation values.
83    ///
84    /// [AmneziaWG Docs](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module?tab=readme-ov-file#configuration)
85    #[cfg(feature = "amneziawg")]
86    #[cfg_attr(docsrs, doc(cfg(feature = "amneziawg")))]
87    pub amnezia_settings: Option<AmneziaSettings>,
88
89    /// Commands, that will be executed before the interface is brought up
90    ///
91    /// [Wireguard docs](https://github.com/pirate/wireguard-docs#preup)
92    pub pre_up: Vec<String>,
93
94    /// Commands, that will be executed before the interface is brought down
95    ///
96    /// [Wireguard docs](https://github.com/pirate/wireguard-docs#predown)
97    pub pre_down: Vec<String>,
98
99    /// Commands, that will be executed after the interface is brought up
100    ///
101    /// [Wireguard docs](https://github.com/pirate/wireguard-docs#postup)
102    pub post_up: Vec<String>,
103
104    /// Commands, that will be executed after the interface is brought down
105    ///
106    /// [Wireguard docs](https://github.com/pirate/wireguard-docs#postdown)
107    pub post_down: Vec<String>,
108
109    /// Peers.
110    ///
111    /// Create them using [`PeerBuilder`] or [`Interface::to_peer`] method.
112    ///
113    /// [Wireguard docs](https://github.com/pirate/wireguard-docs#peer)
114    pub peers: Vec<Peer>,
115}
116
117impl Interface {
118    /// Get [`Peer`] from interface.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// # use wireguard_conf::prelude::*;
124    /// // Create server node
125    /// let mut server = InterfaceBuilder::new()
126    ///     // <snip>
127    ///     .build();
128    ///
129    /// // Create client node, and add server to client's peers
130    /// let client = InterfaceBuilder::new()
131    ///     // <snip>
132    ///     .add_peer(server.to_peer()) // convert `Interface` to `Peer` using `.to_peer()` method.
133    ///     .build();
134    ///
135    /// // Add client to server's peers
136    /// server.peers.push(client.to_peer());
137    ///
138    /// println!("Server config:\n{server}");
139    /// println!("Client config:\n{client}");
140    /// ```
141    pub fn to_peer(&self) -> Peer {
142        Peer {
143            endpoint: self.endpoint.clone(),
144            allowed_ips: vec![self.address],
145            key: Either::Left(self.private_key.clone()),
146            persistent_keepalive: 0,
147
148            #[cfg(feature = "amneziawg")]
149            amnezia_settings: self.amnezia_settings.clone(),
150        }
151    }
152}
153
154impl fmt::Display for Interface {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        writeln!(f, "[Interface]")?;
157        if let Some(endpoint) = &self.endpoint {
158            writeln!(f, "# Name = {endpoint}")?;
159        }
160        writeln!(f, "Address = {}", self.address)?;
161        if let Some(listen_port) = self.listen_port {
162            writeln!(f, "ListenPort = {listen_port}")?;
163        }
164        writeln!(f, "PrivateKey = {}", self.private_key)?;
165        if !self.dns.is_empty() {
166            writeln!(f, "DNS = {}", self.dns.join(","))?;
167        }
168        if let Some(table) = &self.table {
169            writeln!(f, "Table = {table}")?;
170        }
171        if let Some(mtu) = &self.mtu {
172            writeln!(f, "MTU = {mtu}")?;
173        }
174
175        if !self.pre_up.is_empty() {
176            writeln!(f)?;
177            for snippet in &self.pre_up {
178                writeln!(f, "PreUp = {snippet}")?;
179            }
180        }
181        if !self.pre_down.is_empty() {
182            writeln!(f)?;
183            for snippet in &self.pre_down {
184                writeln!(f, "PreDown = {snippet}")?;
185            }
186        }
187        if !self.post_up.is_empty() {
188            writeln!(f)?;
189            for snippet in &self.post_up {
190                writeln!(f, "PostUp = {snippet}")?;
191            }
192        }
193        if !self.post_down.is_empty() {
194            writeln!(f)?;
195            for snippet in &self.post_down {
196                writeln!(f, "PostDown = {snippet}")?;
197            }
198        }
199
200        #[cfg(feature = "amneziawg")]
201        if let Some(amnezia_settings) = &self.amnezia_settings {
202            writeln!(f)?;
203            writeln!(f, "{amnezia_settings}")?;
204        }
205
206        for peer in &self.peers {
207            writeln!(f)?;
208            writeln!(f, "{peer}")?;
209        }
210
211        fmt::Result::Ok(())
212    }
213}