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}