1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! # DBus interface proxy for: `net.openvpn.v3.netcfg`
//!
//! This code was generated by `zbus-xmlgen` `3.1.0` from DBus introspection data.
//! Source: `net.openvpn.v3.netcfg.xml`.

use self::constants::*;
use super::netcfg_node::{NetCfgNodeProxy, NetCfgNodeProxyBlocking};
use crate::log::constants::{LogCategory, LogGroup, LogLevel};
use enumflags2::BitFlags;
use std::fmt;
use zbus::dbus_proxy;

#[dbus_proxy(
    interface = "net.openvpn.v3.netcfg",
    default_service = "net.openvpn.v3.netcfg",
    default_path = "/net/openvpn/v3/netcfg"
)]
trait NetCfg {
    /// Cleanup method
    ///
    /// This method will remove/cleanup any resources still held by the calling PID.
    fn cleanup(&self) -> zbus::Result<()>;

    /// CreateVirtualInterface method
    ///
    /// Create a virtual interface and return the object path of the new interface.
    ///
    /// # Arguments
    ///
    /// * `device_name` - A user friendly name for the device, will be part of device_path.
    ///
    /// # Returns
    ///
    /// A unique D-Bus object path for create device.
    #[dbus_proxy(object = "NetCfgNode")]
    fn create_virtual_interface(&self, device_name: &str);

    /// DcoAvailable method
    ///
    /// This method is called by the VPN client backend to check if the DCO kernel module is available. It it called by through the tun_builder interface, to query the status during instantiation of the transport used to establish the tunnel.
    ///
    /// # Returns
    ///
    /// True if the DCO kernel module is available and loadable.
    fn dco_available(&self) -> zbus::Result<bool>;

    /// FetchInterfaceList method
    ///
    /// This method will return an array of object paths to virtual interfaces the caller is granted access to.
    ///
    /// # Returns
    ///
    /// An array of object paths to accessible virtual interfaces.
    fn fetch_interface_list(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;

    /// NotificationSubscribe method
    ///
    /// A service which wants to respond to various network change activities triggered by OpenVPN can subscribe to the `net.openvpn.v3.netcfg.NetworkChange` signal. Such subscriptions are handled by calling this method.
    ///
    /// # Arguments
    ///
    /// `filter` - A filter mask defining which NetworkChange events to subscribe to. Valid values are `1` to `2047`.
    fn notification_subscribe(&self, filter: BitFlags<NetCfgChangeType>) -> zbus::Result<()>;

    /// NotificationSubscriberList method
    ///
    /// Retrieves a list of all active subscriptions together with their filter mask.
    ///
    /// This method is restricted to the *root* user.
    ///
    /// # Returns
    ///
    /// An array of tuples with the subscribers unique D-Bus name and the attached filter mask.
    fn notification_subscriber_list(
        &self,
    ) -> zbus::Result<Vec<(String, BitFlags<NetCfgChangeType>)>>;

    /// NotificationUnsubscribe method
    ///
    /// Any services who has subscribed to NetworkChange signals must unsubscribe before disconnecting from the D-Bus. This is done by calling this method.
    ///
    /// The subscriber argument this method needs should always be an empty string. Processes running as *root* can send the the unique D-Bus name to forcefully unsubscribe a specific subscription.
    ///
    /// # Arguments
    ///
    /// * `optional_subscriber` - This should be empty for non-root users. Must otherwise contain a valid unique D-Bus name.
    fn notification_unsubscribe(&self, optional_subscriber: &str) -> zbus::Result<()>;

    /// ProtectSocket method
    ///
    /// This method is called by the service client to signal that a socket needs to be protected from being routed over the VPN to avoid routing loops. The method of how this is actually implemented can be controlled by command line arguments to the netcfg service process.
    ///
    /// # Arguments
    ///
    /// * File descriptor of the socket to protect (Unix file descriptors that are passed are not in the D-Bus method signature). Only the first provided fd is being processed.
    /// * `remote`- The remote host this socket is connected to.
    /// * `ipv6`- ?
    /// * `device_path`- If an tun device is already opened, ignore routes from this device
    fn protect_socket(
        &self,
        remote: &str,
        ipv6: bool,
        device_path: &zbus::zvariant::ObjectPath<'_>,
    ) -> zbus::Result<bool>;

    /// Log signal
    ///
    /// Whenever the network configuration service needs to log something, it issues a Log signal which carries a log group, log verbosity level and a string with the log message itself.
    #[dbus_proxy(signal)]
    fn log(&self, group: LogGroup, category: LogCategory, message: &str) -> zbus::Result<()>;

    /// Filename of the config file netcfg has parsed at start-up.
    #[dbus_proxy(property, name = "config_file")]
    fn config_file(&self) -> zbus::Result<String>;

    /// DNS search domains in used, pushed from all VPN sessions.
    #[dbus_proxy(property, name = "global_dns_search")]
    fn global_dns_search(&self) -> zbus::Result<u32>;

    /// DNS servers in use, pushed from all VPN sessions.
    #[dbus_proxy(property, name = "global_dns_servers")]
    fn global_dns_servers(&self) -> zbus::Result<u32>;

    /// Controls the log verbosity of messages intended to be proxied to the user frontend.
    ///
    /// **Note:** Not currently implemented.
    #[dbus_proxy(property, name = "log_level")]
    fn log_level(&self) -> zbus::Result<LogLevel>;
    fn set_log_level(&self, value: LogLevel) -> zbus::Result<()>;

    /// Version information about the running service.
    #[dbus_proxy(property, name = "version")]
    fn version(&self) -> zbus::Result<String>;
}

impl fmt::Display for LogArgs<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "[{}] ({}) - {}",
            self.category(),
            self.group(),
            self.message()
        )
    }
}

pub mod constants {
    use std::fmt;

    use enumflags2::bitflags;
    use serde::{Deserialize, Serialize};
    use static_assertions::assert_impl_all;
    use zbus::zvariant::Type;

    /// Network Configuration Change Type
    ///
    /// Source: openvpn3-linux/src/netcfg/netcfg-changetype.hpp
    #[bitflags]
    #[repr(u16)]
    #[derive(Type, Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
    pub enum NetCfgChangeType {
        ///	A new virtual interface has been added on the system
        DeviceAdded = 0x001,
        ///	A virtual interface has been removed from the system
        DeviceRemoved = 0x002,
        ///	An IP address has been added to a virtual interface
        IpaddrAdded = 0x004,
        ///	An IP address has been removed from the virtual interface
        IpaddrRemoved = 0x008,
        ///	A route has been added to the routing table, related to this interface
        RouteAdded = 0x010,
        ///	A route has been remove from the routing table, related to this interface
        RouteRemoved = 0x020,
        ///	A route has been excluded from the routing table, related to this interface
        RouteExcluded = 0x040,
        ///	A DNS server has been added to the DNS configuration
        DnsServerAdded = 0x080,
        ///	A DNS server has been removed from the DNS configuration
        DnsServerRemoved = 0x100,
        ///	A DNS search domain has been added to the DNS configuration
        DnsSearchAdded = 0x200,
        ///	A DNS search domain has been removed from the DNS configuration
        DnsSearchRemoved = 0x400,
    }

    assert_impl_all!(NetCfgChangeType: Send, Sync, Unpin);

    impl fmt::Display for NetCfgChangeType {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Self::DeviceAdded => write!(f, "Device Added"),
                Self::DeviceRemoved => write!(f, "Device Removed"),
                Self::IpaddrAdded => write!(f, "IP Address Added"),
                Self::IpaddrRemoved => write!(f, "IP Address Removed"),
                Self::RouteAdded => write!(f, "Route Added"),
                Self::RouteRemoved => write!(f, "Route Removed"),
                Self::RouteExcluded => write!(f, "Route Excluded"),
                Self::DnsServerAdded => write!(f, "DNS Server Added"),
                Self::DnsServerRemoved => write!(f, "DNS Server Removed"),
                Self::DnsSearchAdded => write!(f, "DNS Search Added"),
                Self::DnsSearchRemoved => write!(f, "DNS Search Removed"),
            }
        }
    }
}