use std::{ net::SocketAddr, time::{Duration, SystemTime}, }; use ipnet::IpNet; use netlink_packet_wireguard::{ WireguardAllowedIp, WireguardAllowedIpAttr, WireguardAttribute, WireguardMessage, WireguardPeer, WireguardPeerAttribute, }; use super::{Error, Key, Result}; #[derive(Debug, Clone)] pub struct DeviceView { pub name: String, pub ifindex: u32, pub private_key: Option, pub public_key: Option, pub listen_port: u16, pub fwmark: u32, pub peers: Vec, } #[derive(Debug, Clone)] pub struct PeerView { pub public_key: Key, pub preshared_key: Option, pub endpoint: Option, pub persistent_keepalive: Option, pub last_handshake: Option, pub rx_bytes: u64, pub tx_bytes: u64, pub allowed_ips: Vec, } pub(super) fn device_view_from_payload(wg: WireguardMessage) -> Result { let mut if_index = None; let mut if_name = None; let mut private_key = None; let mut public_key = None; let mut listen_port = None; let mut fwmark = None; let mut peers = None; for attr in wg.attributes { match attr { WireguardAttribute::IfIndex(v) => if_index = Some(v), WireguardAttribute::IfName(v) => if_name = Some(v), WireguardAttribute::PrivateKey(v) => private_key = Some(Key::from(v)), WireguardAttribute::PublicKey(v) => public_key = Some(Key::from(v)), WireguardAttribute::ListenPort(v) => listen_port = Some(v), WireguardAttribute::Fwmark(v) => fwmark = Some(v), WireguardAttribute::Peers(v) => peers = Some(peers_from_wg_peers(v)?), _ => {} } } Ok(DeviceView { name: if_name.ok_or_else(|| Error::message("missing if_name"))?, ifindex: if_index.ok_or_else(|| Error::message("missing if_index"))?, private_key, public_key, listen_port: listen_port.ok_or_else(|| Error::message("missing listen_port"))?, fwmark: fwmark.ok_or_else(|| Error::message("missing fwmark"))?, peers: peers.unwrap_or_default(), }) } fn peers_from_wg_peers(wg_peers: Vec) -> Result> { let mut peers = Vec::with_capacity(wg_peers.len()); for wg_peer in wg_peers { peers.push(peer_from_wg_peer(wg_peer)?); } Ok(peers) } fn peer_from_wg_peer(wg_peer: WireguardPeer) -> Result { let mut public_key = None; let mut preshared_key = None; let mut endpoint = None; let mut persistent_keepalive = None; let mut last_handshake = None; let mut rx_bytes = None; let mut tx_bytes = None; let mut allowed_ips = Vec::default(); for attr in wg_peer.iter() { match attr { WireguardPeerAttribute::PublicKey(v) => public_key = Some(Key::from(v)), WireguardPeerAttribute::PresharedKey(v) => preshared_key = Some(Key::from(v)), WireguardPeerAttribute::Endpoint(v) => endpoint = Some(*v), WireguardPeerAttribute::PersistentKeepalive(v) => persistent_keepalive = Some(*v), WireguardPeerAttribute::LastHandshake(v) => last_handshake = Some(*v), WireguardPeerAttribute::RxBytes(v) => rx_bytes = Some(*v), WireguardPeerAttribute::TxBytes(v) => tx_bytes = Some(*v), WireguardPeerAttribute::AllowedIps(v) => { for ip in v { allowed_ips.push(ipnet_from_wg(ip)?); } } _ => {} } } let last_handshake = last_handshake.ok_or_else(|| Error::message("missing last_handshake"))?; let rx_bytes = rx_bytes.ok_or_else(|| Error::message("missing rx_bytes"))?; let tx_bytes = tx_bytes.ok_or_else(|| Error::message("missing tx_bytes"))?; let handshake_is_zero = last_handshake.seconds == 0 && last_handshake.nano_seconds == 0; let last_handshake = if handshake_is_zero && (rx_bytes == 0 || tx_bytes == 0) { None } else { let duration = Duration::new( last_handshake.seconds as u64, last_handshake.nano_seconds as u32, ); Some( SystemTime::UNIX_EPOCH .checked_add(duration) .ok_or_else(|| Error::message("invalid last_handshake"))?, ) }; Ok(PeerView { public_key: public_key.ok_or_else(|| Error::message("missing public_key"))?, preshared_key, endpoint, persistent_keepalive, last_handshake, rx_bytes, tx_bytes, allowed_ips, }) } fn ipnet_from_wg(wg: &WireguardAllowedIp) -> Result { let mut ip = None; let mut prefix = None; for attr in wg.iter() { match attr { WireguardAllowedIpAttr::IpAddr(v) => ip = Some(*v), WireguardAllowedIpAttr::Cidr(v) => prefix = Some(*v), _ => {} } } Ok(IpNet::new( ip.ok_or_else(|| Error::message("missing ip"))?, prefix.ok_or_else(|| Error::message("missing prefix"))?, ) .map_err(|e| Error::with_message(e, "invalid ipnet"))?) }