use super::types::{
OvpnAttr, OvpnCipherAlg, OvpnCmd, OvpnDelPeerReason, OvpnKeySlot, OvpnKeyconfAttr,
OvpnKeydirAttr, OvpnPeerAttr,
};
use crate::macros::{GenlMessage, NetlinkAttrs};
#[derive(NetlinkAttrs, Debug, Default, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct OvpnKeydir {
#[genl_attr(OvpnKeydirAttr::CipherKey)]
pub cipher_key: Option<Vec<u8>>,
#[genl_attr(OvpnKeydirAttr::NonceTail)]
pub nonce_tail: Option<Vec<u8>>,
}
impl OvpnKeydir {
pub fn new(cipher_key: impl Into<Vec<u8>>, nonce_tail: impl Into<Vec<u8>>) -> Self {
Self {
cipher_key: Some(cipher_key.into()),
nonce_tail: Some(nonce_tail.into()),
}
}
}
#[derive(NetlinkAttrs, Debug, Default, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct OvpnKeyconf {
#[genl_attr(OvpnKeyconfAttr::PeerId)]
pub peer_id: Option<u32>,
#[genl_attr(OvpnKeyconfAttr::Slot, repr = "u32")]
pub slot: Option<OvpnKeySlot>,
#[genl_attr(OvpnKeyconfAttr::KeyId)]
pub key_id: Option<u32>,
#[genl_attr(OvpnKeyconfAttr::CipherAlg, repr = "u32")]
pub cipher_alg: Option<OvpnCipherAlg>,
#[genl_attr(OvpnKeyconfAttr::EncryptDir, nested)]
pub encrypt_dir: Option<OvpnKeydir>,
#[genl_attr(OvpnKeyconfAttr::DecryptDir, nested)]
pub decrypt_dir: Option<OvpnKeydir>,
}
impl OvpnKeyconf {
pub fn new(
peer_id: u32,
slot: OvpnKeySlot,
key_id: u32,
cipher_alg: OvpnCipherAlg,
encrypt_dir: OvpnKeydir,
decrypt_dir: OvpnKeydir,
) -> Self {
Self {
peer_id: Some(peer_id),
slot: Some(slot),
key_id: Some(key_id),
cipher_alg: Some(cipher_alg),
encrypt_dir: Some(encrypt_dir),
decrypt_dir: Some(decrypt_dir),
}
}
pub fn identity(peer_id: u32, slot: OvpnKeySlot) -> Self {
Self {
peer_id: Some(peer_id),
slot: Some(slot),
..Self::default()
}
}
pub fn swap_identity(peer_id: u32) -> Self {
Self {
peer_id: Some(peer_id),
..Self::default()
}
}
}
#[derive(NetlinkAttrs, Debug, Default, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct OvpnPeer {
#[genl_attr(OvpnPeerAttr::Id)]
pub id: Option<u32>,
#[genl_attr(OvpnPeerAttr::RemoteIpv4)]
pub remote_ipv4: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::RemoteIpv6)]
pub remote_ipv6: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::RemoteIpv6ScopeId)]
pub remote_ipv6_scope_id: Option<u32>,
#[genl_attr(OvpnPeerAttr::RemotePort)]
pub remote_port: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::Socket)]
pub socket: Option<u32>,
#[genl_attr(OvpnPeerAttr::SocketNetnsid)]
pub socket_netnsid: Option<i32>,
#[genl_attr(OvpnPeerAttr::VpnIpv4)]
pub vpn_ipv4: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::VpnIpv6)]
pub vpn_ipv6: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::LocalIpv4)]
pub local_ipv4: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::LocalIpv6)]
pub local_ipv6: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::LocalPort)]
pub local_port: Option<Vec<u8>>,
#[genl_attr(OvpnPeerAttr::KeepaliveInterval)]
pub keepalive_interval: Option<u32>,
#[genl_attr(OvpnPeerAttr::KeepaliveTimeout)]
pub keepalive_timeout: Option<u32>,
#[genl_attr(OvpnPeerAttr::DelReason, repr = "u32")]
pub del_reason: Option<OvpnDelPeerReason>,
#[genl_attr(OvpnPeerAttr::VpnRxBytes)]
pub vpn_rx_bytes: Option<u64>,
#[genl_attr(OvpnPeerAttr::VpnTxBytes)]
pub vpn_tx_bytes: Option<u64>,
#[genl_attr(OvpnPeerAttr::VpnRxPackets)]
pub vpn_rx_packets: Option<u64>,
#[genl_attr(OvpnPeerAttr::VpnTxPackets)]
pub vpn_tx_packets: Option<u64>,
#[genl_attr(OvpnPeerAttr::LinkRxBytes)]
pub link_rx_bytes: Option<u64>,
#[genl_attr(OvpnPeerAttr::LinkTxBytes)]
pub link_tx_bytes: Option<u64>,
#[genl_attr(OvpnPeerAttr::LinkRxPackets)]
pub link_rx_packets: Option<u64>,
#[genl_attr(OvpnPeerAttr::LinkTxPackets)]
pub link_tx_packets: Option<u64>,
#[genl_attr(OvpnPeerAttr::TxId)]
pub tx_id: Option<u32>,
}
impl OvpnPeer {
pub fn identity(id: u32) -> Self {
Self {
id: Some(id),
..Self::default()
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::PeerNew)]
pub struct OvpnPeerNewRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Peer, nested)]
pub peer: Option<OvpnPeer>,
}
impl OvpnPeerNewRequest {
pub fn new(ifindex: u32, peer: OvpnPeer) -> Self {
Self {
ifindex,
peer: Some(peer),
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::PeerSet)]
pub struct OvpnPeerSetRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Peer, nested)]
pub peer: Option<OvpnPeer>,
}
impl OvpnPeerSetRequest {
pub fn new(ifindex: u32, peer: OvpnPeer) -> Self {
Self {
ifindex,
peer: Some(peer),
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::PeerGet)]
pub struct OvpnPeerGetRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Peer, nested)]
pub peer: Option<OvpnPeer>,
}
impl OvpnPeerGetRequest {
pub fn by_id(ifindex: u32, peer_id: u32) -> Self {
Self {
ifindex,
peer: Some(OvpnPeer::identity(peer_id)),
}
}
pub fn dump(ifindex: u32) -> Self {
Self {
ifindex,
peer: None,
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::PeerGet)]
#[non_exhaustive]
pub struct OvpnPeerReply {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Peer, nested)]
pub peer: Option<OvpnPeer>,
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::PeerDel)]
pub struct OvpnPeerDelRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Peer, nested)]
pub peer: Option<OvpnPeer>,
}
impl OvpnPeerDelRequest {
pub fn new(ifindex: u32, peer_id: u32) -> Self {
Self {
ifindex,
peer: Some(OvpnPeer::identity(peer_id)),
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::KeyNew)]
pub struct OvpnKeyNewRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Keyconf, nested)]
pub keyconf: Option<OvpnKeyconf>,
}
impl OvpnKeyNewRequest {
pub fn new(ifindex: u32, keyconf: OvpnKeyconf) -> Self {
Self {
ifindex,
keyconf: Some(keyconf),
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::KeyGet)]
pub struct OvpnKeyGetRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Keyconf, nested)]
pub keyconf: Option<OvpnKeyconf>,
}
impl OvpnKeyGetRequest {
pub fn new(ifindex: u32, peer_id: u32, slot: OvpnKeySlot) -> Self {
Self {
ifindex,
keyconf: Some(OvpnKeyconf::identity(peer_id, slot)),
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::KeyGet)]
#[non_exhaustive]
pub struct OvpnKeyReply {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Keyconf, nested)]
pub keyconf: Option<OvpnKeyconf>,
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::KeySwap)]
pub struct OvpnKeySwapRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Keyconf, nested)]
pub keyconf: Option<OvpnKeyconf>,
}
impl OvpnKeySwapRequest {
pub fn new(ifindex: u32, peer_id: u32) -> Self {
Self {
ifindex,
keyconf: Some(OvpnKeyconf::swap_identity(peer_id)),
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = OvpnCmd::KeyDel)]
pub struct OvpnKeyDelRequest {
#[genl_attr(OvpnAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(OvpnAttr::Keyconf, nested)]
pub keyconf: Option<OvpnKeyconf>,
}
impl OvpnKeyDelRequest {
pub fn new(ifindex: u32, peer_id: u32, slot: OvpnKeySlot) -> Self {
Self {
ifindex,
keyconf: Some(OvpnKeyconf::identity(peer_id, slot)),
}
}
}
impl OvpnPeer {
pub fn encode_ipv4(addr: std::net::Ipv4Addr) -> Vec<u8> {
addr.octets().to_vec()
}
pub fn decode_ipv4(bytes: &[u8]) -> Option<std::net::Ipv4Addr> {
if bytes.len() != 4 {
return None;
}
Some(std::net::Ipv4Addr::new(
bytes[0], bytes[1], bytes[2], bytes[3],
))
}
pub fn encode_ipv6(addr: std::net::Ipv6Addr) -> Vec<u8> {
addr.octets().to_vec()
}
pub fn decode_ipv6(bytes: &[u8]) -> Option<std::net::Ipv6Addr> {
if bytes.len() != 16 {
return None;
}
let mut octets = [0u8; 16];
octets.copy_from_slice(bytes);
Some(std::net::Ipv6Addr::from(octets))
}
pub fn encode_port(port: u16) -> Vec<u8> {
port.to_be_bytes().to_vec()
}
pub fn decode_port(bytes: &[u8]) -> Option<u16> {
if bytes.len() != 2 {
return None;
}
Some(u16::from_be_bytes([bytes[0], bytes[1]]))
}
pub fn set_remote_v4(&mut self, addr: std::net::SocketAddrV4) {
self.remote_ipv4 = Some(Self::encode_ipv4(*addr.ip()));
self.remote_port = Some(Self::encode_port(addr.port()));
}
pub fn set_remote_v6(&mut self, addr: std::net::SocketAddrV6) {
self.remote_ipv6 = Some(Self::encode_ipv6(*addr.ip()));
self.remote_port = Some(Self::encode_port(addr.port()));
if addr.scope_id() != 0 {
self.remote_ipv6_scope_id = Some(addr.scope_id());
}
}
pub fn remote_socket(&self) -> Option<std::net::SocketAddr> {
let port = self.remote_port.as_deref().and_then(Self::decode_port)?;
if let Some(v4) = self.remote_ipv4.as_deref().and_then(Self::decode_ipv4) {
return Some(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
v4, port,
)));
}
if let Some(v6) = self.remote_ipv6.as_deref().and_then(Self::decode_ipv6) {
let scope = self.remote_ipv6_scope_id.unwrap_or(0);
return Some(std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
v6, port, 0, scope,
)));
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
macros::{__rt, NetlinkAttrs},
netlink::MessageBuilder,
};
fn body(write: impl FnOnce(&mut MessageBuilder)) -> Vec<u8> {
let mut b = MessageBuilder::new(0, 0);
let start = b.len();
write(&mut b);
b.as_bytes()[start..].to_vec()
}
#[test]
fn keydir_round_trips() {
let original = OvpnKeydir::new([0x11; 32], [0x22; 8]);
let body = body(|b| original.write_attrs(b).unwrap());
let parsed = OvpnKeydir::read_attrs(&body).expect("parse");
assert_eq!(parsed, original);
}
#[test]
fn keyconf_round_trips_full() {
let original = OvpnKeyconf::new(
42,
OvpnKeySlot::Primary,
3,
OvpnCipherAlg::AesGcm,
OvpnKeydir::new(vec![1; 32], vec![2; 8]),
OvpnKeydir::new(vec![3; 32], vec![4; 8]),
);
let body = body(|b| original.write_attrs(b).unwrap());
let parsed = OvpnKeyconf::read_attrs(&body).expect("parse");
assert_eq!(parsed.peer_id, Some(42));
assert_eq!(parsed.slot, Some(OvpnKeySlot::Primary));
assert_eq!(parsed.key_id, Some(3));
assert_eq!(parsed.cipher_alg, Some(OvpnCipherAlg::AesGcm));
assert_eq!(parsed.encrypt_dir, original.encrypt_dir);
assert_eq!(parsed.decrypt_dir, original.decrypt_dir);
}
#[test]
fn keyconf_identity_only_emits_peer_and_slot() {
let original = OvpnKeyconf::identity(7, OvpnKeySlot::Secondary);
let body = body(|b| original.write_attrs(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(t, _)| t).collect();
assert_eq!(
attrs,
vec![OvpnKeyconfAttr::PeerId as u16, OvpnKeyconfAttr::Slot as u16,]
);
}
#[test]
fn peer_identity_emits_only_id() {
let peer = OvpnPeer::identity(99);
let body = body(|b| peer.write_attrs(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(t, _)| t).collect();
assert_eq!(attrs, vec![OvpnPeerAttr::Id as u16]);
}
#[test]
fn ipv4_encode_decode_round_trips() {
let addr = std::net::Ipv4Addr::new(10, 0, 0, 99);
let bytes = OvpnPeer::encode_ipv4(addr);
assert_eq!(bytes, vec![10, 0, 0, 99]);
assert_eq!(OvpnPeer::decode_ipv4(&bytes), Some(addr));
assert_eq!(OvpnPeer::decode_ipv4(&[1, 2, 3]), None);
}
#[test]
fn ipv6_encode_decode_round_trips() {
let addr: std::net::Ipv6Addr = "fe80::1".parse().unwrap();
let bytes = OvpnPeer::encode_ipv6(addr);
assert_eq!(bytes.len(), 16);
assert_eq!(OvpnPeer::decode_ipv6(&bytes), Some(addr));
assert_eq!(OvpnPeer::decode_ipv6(&[0; 4]), None);
}
#[test]
fn port_encode_decode_is_big_endian() {
let bytes = OvpnPeer::encode_port(1194);
assert_eq!(bytes, vec![0x04, 0xAA]);
assert_eq!(OvpnPeer::decode_port(&bytes), Some(1194));
assert_eq!(OvpnPeer::decode_port(&[1, 2, 3]), None);
}
#[test]
fn set_remote_v4_populates_both_fields() {
let mut peer = OvpnPeer::identity(1);
peer.set_remote_v4("10.0.0.1:1194".parse().unwrap());
let sock = peer.remote_socket().expect("decode");
assert_eq!(sock, "10.0.0.1:1194".parse().unwrap());
}
#[test]
fn set_remote_v6_populates_address_and_port() {
let mut peer = OvpnPeer::identity(2);
peer.set_remote_v6("[2001:db8::1]:1194".parse().unwrap());
let sock = peer.remote_socket().expect("decode");
assert_eq!(sock, "[2001:db8::1]:1194".parse().unwrap());
}
#[test]
fn peer_new_request_emits_ifindex_and_nested_peer() {
let req = OvpnPeerNewRequest::new(7, OvpnPeer::identity(42));
let body = body(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(t, _)| t).collect();
assert_eq!(attrs, vec![OvpnAttr::Ifindex as u16, OvpnAttr::Peer as u16]);
}
#[test]
fn peer_get_dump_omits_peer_nest() {
let req = OvpnPeerGetRequest::dump(7);
let body = body(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(t, _)| t).collect();
assert_eq!(attrs, vec![OvpnAttr::Ifindex as u16]);
}
#[test]
fn peer_get_by_id_emits_peer_nest() {
let req = OvpnPeerGetRequest::by_id(3, 99);
let body = body(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(t, _)| t).collect();
assert_eq!(attrs, vec![OvpnAttr::Ifindex as u16, OvpnAttr::Peer as u16]);
}
#[test]
fn peer_socket_and_netnsid_round_trip() {
let mut peer = OvpnPeer::identity(21);
peer.socket = Some(42);
peer.socket_netnsid = Some(3);
let original = OvpnPeerReply {
ifindex: 9,
peer: Some(peer),
};
let body = body(|b| original.to_bytes(b).unwrap());
let parsed = OvpnPeerReply::from_bytes(&body).expect("parse");
let p = parsed.peer.expect("peer present");
assert_eq!(p.id, Some(21));
assert_eq!(p.socket, Some(42));
assert_eq!(p.socket_netnsid, Some(3));
}
#[test]
fn peer_reply_round_trips_counters() {
let mut peer = OvpnPeer::identity(5);
peer.vpn_rx_bytes = Some(1_000_000);
peer.link_tx_packets = Some(50);
peer.set_remote_v4("192.168.1.50:1194".parse().unwrap());
let original = OvpnPeerReply {
ifindex: 7,
peer: Some(peer.clone()),
};
let body = body(|b| original.to_bytes(b).unwrap());
let parsed = OvpnPeerReply::from_bytes(&body).expect("parse");
assert_eq!(parsed.ifindex, 7);
let parsed_peer = parsed.peer.expect("peer present");
assert_eq!(parsed_peer.id, Some(5));
assert_eq!(parsed_peer.vpn_rx_bytes, Some(1_000_000));
assert_eq!(parsed_peer.link_tx_packets, Some(50));
assert_eq!(
parsed_peer.remote_socket(),
Some("192.168.1.50:1194".parse().unwrap())
);
}
#[test]
fn key_swap_request_only_emits_peer_id_inside_keyconf() {
let req = OvpnKeySwapRequest::new(11, 42);
let outer_body = body(|b| req.to_bytes(b).unwrap());
let outer: Vec<u16> = __rt::attr_iter(&outer_body).map(|(t, _)| t).collect();
assert_eq!(
outer,
vec![OvpnAttr::Ifindex as u16, OvpnAttr::Keyconf as u16]
);
for (ty, payload) in __rt::attr_iter(&outer_body) {
if ty == OvpnAttr::Keyconf as u16 {
let inner: Vec<u16> = __rt::attr_iter(payload).map(|(t, _)| t).collect();
assert_eq!(inner, vec![OvpnKeyconfAttr::PeerId as u16]);
}
}
}
#[test]
fn key_del_request_emits_peer_id_and_slot() {
let req = OvpnKeyDelRequest::new(11, 42, OvpnKeySlot::Secondary);
let outer_body = body(|b| req.to_bytes(b).unwrap());
for (ty, payload) in __rt::attr_iter(&outer_body) {
if ty == OvpnAttr::Keyconf as u16 {
let inner: Vec<u16> = __rt::attr_iter(payload).map(|(t, _)| t).collect();
assert_eq!(
inner,
vec![OvpnKeyconfAttr::PeerId as u16, OvpnKeyconfAttr::Slot as u16]
);
}
}
}
}