nlink/netlink/genl/ovpn/mod.rs
1//! OpenVPN data-channel offload (DCO) Generic Netlink family.
2//!
3//! The kernel's `ovpn` GENL family (stabilized in **Linux 6.16**)
4//! lets userspace push OpenVPN 2.7 data-channel processing into
5//! the kernel. The TLS handshake stays in userspace; the
6//! pre-derived AEAD keys + per-peer socket descriptors are handed
7//! to the kernel via this family, and packets are encrypted /
8//! decrypted in-kernel from then on. This eliminates the
9//! per-packet user/kernel boundary crossings that bottlenecked
10//! pre-2.7 OpenVPN.
11//!
12//! # Status — Plan 197
13//!
14//! | Phase | Ships |
15//! |---|---|
16//! | Family marker + module scaffold | ✓ |
17//! | Command + attribute + value enums | ✓ |
18//! | Imperative `Connection<Ovpn>` methods | ✓ |
19//! | Multicast `peers` group + `OvpnEvent` | ✓ |
20//! | Declarative `OvpnConfig` + diff + apply | ✓ |
21//! | `attach_socket` SCM_RIGHTS cross-netns | deferred |
22//!
23//! The interface itself is created via RTNL — see
24//! [`OvpnLink`][crate::netlink::link::OvpnLink] (Plan 190 §2.3b).
25//! The GENL family operates on an already-existing ovpn interface
26//! by `ifindex`.
27//!
28//! # Construction
29//!
30//! ```ignore
31//! use nlink::netlink::{Connection, genl::ovpn::Ovpn};
32//!
33//! let conn = Connection::<Ovpn>::new_async().await?;
34//! // Family ID resolved against the kernel "ovpn" registration;
35//! // FamilyNotFound on kernels without CONFIG_OVPN.
36//! ```
37//!
38//! Resolution failure is the common case on stock distro kernels
39//! that don't load the `ovpn` module. Handle via
40//! [`Error::is_not_found`](crate::Error::is_not_found):
41//!
42//! ```ignore
43//! match Connection::<Ovpn>::new_async().await {
44//! Ok(conn) => { /* use it */ }
45//! Err(e) if e.is_not_found() => {
46//! tracing::warn!("OVPN DCO not available on this kernel; skipping");
47//! }
48//! Err(e) => return Err(e),
49//! }
50//! ```
51//!
52//! # Cipher constraints
53//!
54//! The kernel accepts AES-GCM (128 or 256-bit) and
55//! ChaCha20-Poly1305 only — the TLS handshake in OpenVPN 2.7
56//! must negotiate one of these. Legacy CBC + non-AEAD modes are
57//! intentionally not supported in DCO mode.
58
59use crate::macros::genl_family;
60
61pub mod config;
62pub mod connection;
63pub mod events;
64pub mod messages;
65pub mod types;
66
67pub use config::{
68 OvpnConfig, OvpnDiff, OvpnInterfaceConfig, OvpnInterfaceConfigBuilder, OvpnKeyConfig,
69 OvpnPeerConfig, OvpnPeerConfigBuilder,
70};
71pub use events::OvpnEvent;
72pub use messages::{
73 OvpnKeyDelRequest, OvpnKeyGetRequest, OvpnKeyNewRequest, OvpnKeyReply, OvpnKeySwapRequest,
74 OvpnKeyconf, OvpnKeydir, OvpnPeer, OvpnPeerDelRequest, OvpnPeerGetRequest, OvpnPeerNewRequest,
75 OvpnPeerReply, OvpnPeerSetRequest,
76};
77pub use types::{
78 OvpnAttr, OvpnCipherAlg, OvpnCmd, OvpnDelPeerReason, OvpnKeyconfAttr, OvpnKeySlot,
79 OvpnKeydirAttr, OvpnPeerAttr, OVPN_MAX_CIPHER_KEY_LEN, OVPN_MAX_KEY_ID, OVPN_MAX_PEER_ID,
80 OVPN_NONCE_TAIL_SIZE,
81};
82
83/// OVPN Generic Netlink family marker.
84///
85/// Constructed via [`Connection::<Ovpn>::new_async()`][Connection]
86/// — the family ID is resolved against the kernel at connection
87/// time. Returns
88/// [`Error::FamilyNotFound`](crate::Error::FamilyNotFound) on
89/// kernels without OVPN support (kernel < 6.16 or `ovpn` module
90/// not loaded).
91///
92/// [Connection]: crate::netlink::Connection
93#[genl_family(name = "ovpn", version = 1)]
94pub struct Ovpn;
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::netlink::{
100 construction::AsyncConstructible, AsyncProtocolInit, Protocol, ProtocolState,
101 };
102
103 #[test]
104 fn family_marker_carries_expected_name_and_version() {
105 assert_eq!(Ovpn::NAME, "ovpn");
106 assert_eq!(Ovpn::VERSION, 1);
107 }
108
109 #[test]
110 fn default_marker_has_zero_family_id_before_resolution() {
111 let d = Ovpn::default();
112 assert_eq!(d.family_id(), 0);
113 }
114
115 #[test]
116 fn protocol_state_routes_to_generic() {
117 const _: () = {
118 assert!(matches!(Ovpn::PROTOCOL, Protocol::Generic));
119 };
120 }
121
122 fn assert_async_constructible<P: AsyncConstructible>() {}
123 fn assert_async_protocol_init<P: AsyncProtocolInit>() {}
124
125 #[test]
126 fn ovpn_satisfies_async_construction_bounds() {
127 assert_async_constructible::<Ovpn>();
128 assert_async_protocol_init::<Ovpn>();
129 }
130}