Skip to content

Commit 45e5b59

Browse files
authored
Merge dev branch (#121)
1 parent eab71e5 commit 45e5b59

17 files changed

Lines changed: 488 additions & 389 deletions

.gitmodules

Whitespace-only changes.

Cargo.lock

Lines changed: 100 additions & 88 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "defguard_wireguard_rs"
3-
version = "0.8.0"
3+
version = "0.9.0"
44
edition = "2024"
5-
rust-version = "1.85"
5+
rust-version = "1.87"
66
description = "A unified multi-platform high-level API for managing WireGuard interfaces"
77
license = "Apache-2.0"
88
readme = "README.md"
@@ -25,11 +25,11 @@ tracing = "0.1"
2525
tracing-subscriber = "0.3"
2626

2727
[target.'cfg(unix)'.dependencies]
28-
defguard_boringtun = { version = "0.6.0", default-features = false, features = [
28+
defguard_boringtun = { version = "0.6", default-features = false, features = [
2929
"device",
3030
]}
3131
libc = { version = "0.2", default-features = false }
32-
nix = { version = "0.30", features = ["ioctl", "socket"] }
32+
nix = { version = "0.31", features = ["ioctl", "socket"] }
3333

3434
[target.'cfg(target_os = "windows")'.dependencies]
3535
ipnet = "2.11"
@@ -39,12 +39,12 @@ windows = { version = "0.62", features = [
3939
"Win32_Networking_WinSock",
4040
"Win32_System_Com",
4141
] }
42-
wireguard-nt = "0.5.0"
42+
wireguard-nt = "0.5"
4343

4444
[target.'cfg(target_os = "linux")'.dependencies]
4545
netlink-packet-core = "0.8"
4646
netlink-packet-generic = "0.4"
47-
netlink-packet-route = "0.25"
47+
netlink-packet-route = "0.28"
4848
netlink-packet-utils = "0.6"
4949
netlink-packet-wireguard = "0.2"
5050
netlink-sys = "0.8"

examples/client.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::{net::SocketAddr, str::FromStr};
22

33
use defguard_wireguard_rs::{
4-
InterfaceConfiguration, WGApi, WireguardInterfaceApi, host::Peer, key::Key, net::IpAddrMask,
4+
InterfaceConfiguration, WGApi, WireguardInterfaceApi, key::Key, net::IpAddrMask, peer::Peer,
55
};
66
use x25519_dalek::{EphemeralSecret, PublicKey};
77

@@ -46,6 +46,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
4646
port: 12345,
4747
peers: vec![peer],
4848
mtu: None,
49+
fwmark: None,
4950
};
5051

5152
#[cfg(not(windows))]

examples/server.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::str::FromStr;
22

33
use defguard_wireguard_rs::{
4-
InterfaceConfiguration, WGApi, WireguardInterfaceApi, host::Peer, key::Key, net::IpAddrMask,
4+
InterfaceConfiguration, WGApi, WireguardInterfaceApi, key::Key, net::IpAddrMask, peer::Peer,
55
};
66
use x25519_dalek::{EphemeralSecret, PublicKey};
77

@@ -44,6 +44,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
4444
port: 12345,
4545
peers: vec![peer],
4646
mtu: None,
47+
fwmark: None,
4748
};
4849
println!("Prepared interface configuration: {interface_config:?}");
4950

examples/userspace.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use std::{
55
};
66

77
use defguard_wireguard_rs::{
8-
InterfaceConfiguration, Userspace, WGApi, WireguardInterfaceApi, host::Peer, key::Key,
9-
net::IpAddrMask,
8+
InterfaceConfiguration, Userspace, WGApi, WireguardInterfaceApi, key::Key, net::IpAddrMask,
9+
peer::Peer,
1010
};
1111
use x25519_dalek::{EphemeralSecret, PublicKey};
1212

@@ -63,6 +63,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
6363
port: 12345,
6464
peers: vec![peer],
6565
mtu: None,
66+
fwmark: None,
6667
};
6768

6869
#[cfg(not(windows))]

src/bsd/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,7 @@ use self::{
3131
timespec::{pack_timespec, unpack_timespec},
3232
wgio::{WgReadIo, WgWriteIo},
3333
};
34-
use crate::{
35-
IpVersion, Key, WireguardInterfaceError,
36-
host::{Host, Peer},
37-
net::IpAddrMask,
38-
};
34+
use crate::{IpVersion, Key, WireguardInterfaceError, host::Host, net::IpAddrMask, peer::Peer};
3935

4036
// Note: these values differ across different platforms.
4137
const AF_INET: u8 = libc::AF_INET as u8;

src/host.rs

Lines changed: 7 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -5,208 +5,34 @@
55
66
use std::{
77
collections::HashMap,
8-
fmt::{self, Debug, Formatter},
8+
fmt,
99
io::{self, BufRead, BufReader, Read},
1010
net::SocketAddr,
1111
str::FromStr,
1212
time::{Duration, SystemTime},
1313
};
1414

1515
#[cfg(target_os = "linux")]
16-
use netlink_packet_wireguard::{
17-
constants::{WGDEVICE_F_REPLACE_PEERS, WGPEER_F_REPLACE_ALLOWEDIPS},
18-
nlas::{WgAllowedIpAttrs, WgDeviceAttrs, WgPeer, WgPeerAttrs},
19-
};
16+
use netlink_packet_wireguard::{constants::WGDEVICE_F_REPLACE_PEERS, nlas::WgDeviceAttrs};
2017
#[cfg(feature = "serde")]
2118
use serde::{Deserialize, Serialize};
2219

23-
use crate::{error::WireguardInterfaceError, key::Key, net::IpAddrMask, utils::resolve};
24-
25-
/// WireGuard peer representation.
26-
#[derive(Clone, Default, PartialEq)]
27-
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
28-
pub struct Peer {
29-
pub public_key: Key,
30-
pub preshared_key: Option<Key>,
31-
pub protocol_version: Option<u32>,
32-
pub endpoint: Option<SocketAddr>,
33-
pub last_handshake: Option<SystemTime>,
34-
pub tx_bytes: u64,
35-
pub rx_bytes: u64,
36-
pub persistent_keepalive_interval: Option<u16>,
37-
pub allowed_ips: Vec<IpAddrMask>,
38-
}
39-
40-
// implement manually to avoid exposing preshared keys
41-
impl fmt::Debug for Peer {
42-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43-
f.debug_struct("Peer")
44-
.field("public_key", &self.public_key)
45-
.field("protocol_version", &self.protocol_version)
46-
.field("endpoint", &self.endpoint)
47-
.field("last_handshake", &self.last_handshake)
48-
.field("tx_bytes", &self.tx_bytes)
49-
.field("rx_bytes", &self.rx_bytes)
50-
.field(
51-
"persistent_keepalive_interval",
52-
&self.persistent_keepalive_interval,
53-
)
54-
.field("allowed_ips", &self.allowed_ips)
55-
.finish_non_exhaustive()
56-
}
57-
}
58-
59-
impl Peer {
60-
/// Create new `Peer` with a given `public_key`.
61-
#[must_use]
62-
pub fn new(public_key: Key) -> Self {
63-
Self {
64-
public_key,
65-
preshared_key: None,
66-
protocol_version: None,
67-
endpoint: None,
68-
last_handshake: None,
69-
tx_bytes: 0,
70-
rx_bytes: 0,
71-
persistent_keepalive_interval: None,
72-
allowed_ips: Vec::new(),
73-
}
74-
}
75-
76-
pub fn set_allowed_ips(&mut self, allowed_ips: Vec<IpAddrMask>) {
77-
self.allowed_ips = allowed_ips;
78-
}
79-
80-
/// Resolves endpoint address to [`SocketAddr`] and sets the field
81-
pub fn set_endpoint(&mut self, endpoint: &str) -> Result<(), WireguardInterfaceError> {
82-
self.endpoint = Some(resolve(endpoint)?);
83-
Ok(())
84-
}
85-
86-
#[must_use]
87-
pub fn as_uapi_update(&self) -> String {
88-
let mut output = format!("public_key={}\n", self.public_key.to_lower_hex());
89-
if let Some(key) = &self.preshared_key {
90-
output.push_str("preshared_key=");
91-
output.push_str(&key.to_lower_hex());
92-
output.push('\n');
93-
}
94-
if let Some(endpoint) = &self.endpoint {
95-
output.push_str("endpoint=");
96-
output.push_str(&endpoint.to_string());
97-
output.push('\n');
98-
}
99-
if let Some(interval) = &self.persistent_keepalive_interval {
100-
output.push_str("persistent_keepalive_interval=");
101-
output.push_str(&interval.to_string());
102-
output.push('\n');
103-
}
104-
output.push_str("replace_allowed_ips=true\n");
105-
for allowed_ip in &self.allowed_ips {
106-
output.push_str("allowed_ip=");
107-
output.push_str(&allowed_ip.to_string());
108-
output.push('\n');
109-
}
110-
111-
output
112-
}
113-
114-
#[must_use]
115-
pub fn as_uapi_remove(&self) -> String {
116-
format!(
117-
"public_key={}\nremove=true\n",
118-
self.public_key.to_lower_hex()
119-
)
120-
}
121-
}
122-
123-
#[cfg(target_os = "linux")]
124-
impl Peer {
125-
#[must_use]
126-
pub(crate) fn from_nlas(nlas: &[WgPeerAttrs]) -> Self {
127-
let mut peer = Self::default();
128-
129-
for nla in nlas {
130-
match nla {
131-
WgPeerAttrs::PublicKey(value) => peer.public_key = Key::new(*value),
132-
WgPeerAttrs::PresharedKey(value) => peer.preshared_key = Some(Key::new(*value)),
133-
WgPeerAttrs::Endpoint(value) => peer.endpoint = Some(*value),
134-
WgPeerAttrs::PersistentKeepalive(value) => {
135-
peer.persistent_keepalive_interval = Some(*value);
136-
}
137-
WgPeerAttrs::LastHandshake(value) => peer.last_handshake = Some(*value),
138-
WgPeerAttrs::RxBytes(value) => peer.rx_bytes = *value,
139-
WgPeerAttrs::TxBytes(value) => peer.tx_bytes = *value,
140-
WgPeerAttrs::AllowedIps(nlas) => {
141-
for nla in nlas {
142-
let ip = nla.iter().find_map(|nla| match nla {
143-
WgAllowedIpAttrs::IpAddr(ip) => Some(*ip),
144-
_ => None,
145-
});
146-
let cidr = nla.iter().find_map(|nla| match nla {
147-
WgAllowedIpAttrs::Cidr(cidr) => Some(*cidr),
148-
_ => None,
149-
});
150-
if let (Some(ip), Some(cidr)) = (ip, cidr) {
151-
peer.allowed_ips.push(IpAddrMask::new(ip, cidr));
152-
}
153-
}
154-
}
155-
_ => (),
156-
}
157-
}
158-
159-
peer
160-
}
161-
162-
#[must_use]
163-
pub(crate) fn as_nlas(&self, ifname: &str) -> Vec<WgDeviceAttrs> {
164-
vec![
165-
WgDeviceAttrs::IfName(ifname.into()),
166-
WgDeviceAttrs::Peers(vec![self.as_nlas_peer()]),
167-
]
168-
}
169-
170-
#[must_use]
171-
pub(crate) fn as_nlas_peer(&self) -> WgPeer {
172-
let mut attrs = vec![WgPeerAttrs::PublicKey(self.public_key.as_array())];
173-
if let Some(keepalive) = self.persistent_keepalive_interval {
174-
attrs.push(WgPeerAttrs::PersistentKeepalive(keepalive));
175-
}
176-
177-
if let Some(endpoint) = self.endpoint {
178-
attrs.push(WgPeerAttrs::Endpoint(endpoint));
179-
}
180-
181-
if let Some(preshared_key) = &self.preshared_key {
182-
attrs.push(WgPeerAttrs::PresharedKey(preshared_key.as_array()));
183-
}
184-
185-
attrs.push(WgPeerAttrs::Flags(WGPEER_F_REPLACE_ALLOWEDIPS));
186-
let allowed_ips = self
187-
.allowed_ips
188-
.iter()
189-
.map(IpAddrMask::to_nlas_allowed_ip)
190-
.collect();
191-
attrs.push(WgPeerAttrs::AllowedIps(allowed_ips));
192-
193-
WgPeer(attrs)
194-
}
195-
}
20+
use super::{key::Key, peer::Peer};
19621

19722
/// WireGuard host representation.
19823
#[derive(Clone, Default)]
19924
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
20025
pub struct Host {
20126
pub listen_port: u16,
20227
pub private_key: Option<Key>,
28+
// FwMark with value of 0, removes the setting from WireGuard interface.
20329
pub(super) fwmark: Option<u32>,
20430
pub peers: HashMap<Key, Peer>,
20531
}
20632

207-
// implement manually to avoid exposing private keys
208-
impl Debug for Host {
209-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
33+
// Implement `Debug` manually to avoid exposing private keys.
34+
impl fmt::Debug for Host {
35+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21036
f.debug_struct("Host")
21137
.field("listen_port", &self.listen_port)
21238
.field("fwmark", &self.fwmark)

src/lib.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//!
1212
//! ```no_run
1313
//! use x25519_dalek::{EphemeralSecret, PublicKey};
14-
//! use defguard_wireguard_rs::{InterfaceConfiguration, Userspace, WGApi, WireguardInterfaceApi, host::Peer};
14+
//! use defguard_wireguard_rs::{InterfaceConfiguration, Userspace, WGApi, WireguardInterfaceApi, peer::Peer};
1515
//! # use defguard_wireguard_rs::error::WireguardInterfaceError;
1616
//!
1717
//! // Create new API struct for interface
@@ -31,8 +31,9 @@
3131
//! prvkey: "AAECAwQFBgcICQoLDA0OD/Dh0sO0pZaHeGlaSzwtHg8=".to_string(),
3232
//! addresses: vec!["10.6.0.30".parse().unwrap()],
3333
//! port: 12345,
34-
//! peers: vec![],
34+
//! peers: Vec::new(),
3535
//! mtu: None,
36+
//! fwmark: None,
3637
//! };
3738
//! wgapi.configure_interface(&interface_config)?;
3839
//!
@@ -58,6 +59,7 @@ pub mod key;
5859
pub mod net;
5960
#[cfg(target_os = "linux")]
6061
pub(crate) mod netlink;
62+
pub mod peer;
6163
mod utils;
6264
mod wgapi;
6365

@@ -84,12 +86,7 @@ use serde::{Deserialize, Serialize};
8486
pub use wgapi::{Kernel, Userspace, WGApi};
8587
pub use wireguard_interface::WireguardInterfaceApi;
8688

87-
use self::{
88-
error::WireguardInterfaceError,
89-
host::{Host, Peer},
90-
key::Key,
91-
net::IpAddrMask,
92-
};
89+
use self::{error::WireguardInterfaceError, host::Host, key::Key, net::IpAddrMask, peer::Peer};
9390

9491
// Internet Protocol (IP) address variant.
9592
#[derive(Clone, Copy)]
@@ -107,8 +104,11 @@ pub struct InterfaceConfiguration {
107104
pub addresses: Vec<IpAddrMask>,
108105
pub port: u16,
109106
pub peers: Vec<Peer>,
110-
/// Maximum transfer unit. `None` means do not set MTU, but keep the system default.
107+
/// Maximum transmission unit. `None` means: do not set MTU, but keep the system default.
111108
pub mtu: Option<u32>,
109+
/// Firewall mark. `None` means: do not set FwMark, but keep the current value.
110+
/// `Some(0)` removes FwMark from WireGuard interfaces.
111+
pub fwmark: Option<u32>,
112112
}
113113

114114
// Implement `Debug` manually to avoid exposing private keys.
@@ -120,6 +120,7 @@ impl fmt::Debug for InterfaceConfiguration {
120120
.field("port", &self.port)
121121
.field("peers", &self.peers)
122122
.field("mtu", &self.mtu)
123+
.field("fwmark", &self.fwmark)
123124
.finish_non_exhaustive()
124125
}
125126
}
@@ -130,6 +131,7 @@ impl TryFrom<&InterfaceConfiguration> for Host {
130131
fn try_from(config: &InterfaceConfiguration) -> Result<Self, Self::Error> {
131132
let key = config.prvkey.as_str().try_into()?;
132133
let mut host = Host::new(config.port, key);
134+
host.fwmark = config.fwmark;
133135
for peercfg in &config.peers {
134136
let peer = peercfg.clone();
135137
let key: Key = peer.public_key.clone();

0 commit comments

Comments
 (0)