Skip to main content

wayle_network/core/connection/
mod.rs

1mod monitoring;
2mod types;
3
4use std::sync::Arc;
5
6use derive_more::Debug;
7use futures::{Stream, StreamExt};
8use tokio_util::sync::CancellationToken;
9use tracing::warn;
10pub(crate) use types::{ActiveConnectionParams, LiveActiveConnectionParams};
11pub use types::{ActiveConnectionStateChangedEvent, VpnConnectionStateChangedEvent};
12use wayle_core::{Property, unwrap_dbus};
13use wayle_traits::{ModelMonitoring, Reactive};
14use zbus::{Connection, zvariant::OwnedObjectPath};
15
16use crate::{
17    error::Error,
18    proxy::active_connection::{ConnectionActiveProxy, vpn::VPNConnectionProxy},
19    types::{
20        flags::NMActivationStateFlags,
21        states::{
22            NMActiveConnectionState, NMActiveConnectionStateReason, NMVpnConnectionState,
23            NMVpnConnectionStateReason,
24        },
25    },
26};
27
28/// Active network connection in NetworkManager.
29///
30/// Tracks state and configuration of currently active connections,
31/// including devices, IP configuration, and connection properties.
32/// Properties update reactively as connection state changes.
33#[derive(Debug, Clone)]
34pub struct ActiveConnection {
35    #[debug(skip)]
36    pub(crate) zbus_connection: Connection,
37
38    /// Token for cancelling monitoring
39    #[debug(skip)]
40    pub(crate) cancellation_token: Option<CancellationToken>,
41
42    /// Object path for this connection
43    pub object_path: OwnedObjectPath,
44
45    /// The path of the connection object that this ActiveConnection is using.
46    pub connection_path: Property<OwnedObjectPath>,
47
48    /// Specific object associated with the active connection. Reflects the
49    /// object used during connection activation, and will not change over the
50    /// lifetime of the ActiveConnection once set.
51    pub specific_object: Property<OwnedObjectPath>,
52
53    /// The ID of the connection, provided for convenience.
54    pub id: Property<String>,
55
56    /// The UUID of the connection, provided for convenience.
57    pub uuid: Property<String>,
58
59    /// The type of the connection, provided for convenience.
60    pub type_: Property<String>,
61
62    /// Array of object paths representing devices which are part of this active
63    /// connection.
64    pub devices: Property<Vec<OwnedObjectPath>>,
65
66    /// The state of this active connection.
67    pub state: Property<NMActiveConnectionState>,
68
69    /// The state flags of this active connection. See NMActivationStateFlags.
70    pub state_flags: Property<NMActivationStateFlags>,
71
72    /// Whether this active connection is the default IPv4 connection, i.e. whether it
73    /// currently owns the default IPv4 route.
74    pub default: Property<bool>,
75
76    /// Object path of the Ip4Config object describing the configuration of the
77    /// connection. Only valid when the connection is in the
78    /// NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
79    pub ip4_config: Property<OwnedObjectPath>,
80
81    /// Object path of the Dhcp4Config object describing the DHCP options returned by the
82    /// DHCP server (assuming the connection used DHCP). Only valid when the connection is
83    /// in the NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
84    pub dhcp4_config: Property<OwnedObjectPath>,
85
86    /// Whether this active connection is the default IPv6 connection, i.e. whether it
87    /// currently owns the default IPv6 route.
88    pub default6: Property<bool>,
89
90    /// Object path of the Ip6Config object describing the configuration of the
91    /// connection. Only valid when the connection is in the
92    /// NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
93    pub ip6_config: Property<OwnedObjectPath>,
94
95    /// Object path of the Dhcp6Config object describing the DHCP options returned by the
96    /// DHCP server (assuming the connection used DHCP). Only valid when the connection is
97    /// in the NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
98    pub dhcp6_config: Property<OwnedObjectPath>,
99
100    /// Whether this active connection is also a VPN connection.
101    pub vpn: Property<bool>,
102
103    /// The path to the controller device if the connection is a port.
104    pub controller: Property<OwnedObjectPath>,
105}
106
107impl Reactive for ActiveConnection {
108    type Context<'a> = ActiveConnectionParams<'a>;
109    type LiveContext<'a> = LiveActiveConnectionParams<'a>;
110    type Error = Error;
111
112    async fn get(params: Self::Context<'_>) -> Result<Self, Self::Error> {
113        Self::from_path(params.connection, params.path, None).await
114    }
115
116    async fn get_live(params: Self::LiveContext<'_>) -> Result<Arc<Self>, Self::Error> {
117        let active_connection = Self::from_path(
118            params.connection,
119            params.path.clone(),
120            Some(params.cancellation_token.child_token()),
121        )
122        .await?;
123        let active_connection = Arc::new(active_connection);
124
125        active_connection.clone().start_monitoring().await?;
126
127        Ok(active_connection)
128    }
129}
130
131impl ActiveConnection {
132    async fn from_path(
133        connection: &Connection,
134        path: OwnedObjectPath,
135        cancellation_token: Option<CancellationToken>,
136    ) -> Result<Self, Error> {
137        let connection_proxy = ConnectionActiveProxy::new(connection, &path).await?;
138
139        if connection_proxy.connection().await.is_err() {
140            warn!(
141                "Active Connection at path '{}' does not exist.",
142                path.clone()
143            );
144            return Err(Error::ObjectNotFound(path.clone()));
145        }
146
147        let (
148            connection_path,
149            specific_object,
150            id,
151            uuid,
152            type_,
153            devices,
154            state,
155            state_flags,
156            default,
157            ip4_config,
158            dhcp4_config,
159            default6,
160            ip6_config,
161            dhcp6_config,
162            vpn,
163            controller,
164        ) = tokio::join!(
165            connection_proxy.connection(),
166            connection_proxy.specific_object(),
167            connection_proxy.id(),
168            connection_proxy.uuid(),
169            connection_proxy.type_(),
170            connection_proxy.devices(),
171            connection_proxy.state(),
172            connection_proxy.state_flags(),
173            connection_proxy.default(),
174            connection_proxy.ip4_config(),
175            connection_proxy.dhcp4_config(),
176            connection_proxy.default6(),
177            connection_proxy.ip6_config(),
178            connection_proxy.dhcp6_config(),
179            connection_proxy.vpn(),
180            connection_proxy.controller(),
181        );
182
183        let connection_path = unwrap_dbus!(connection_path, path);
184        let specific_object = unwrap_dbus!(specific_object, path);
185        let id = unwrap_dbus!(id, path);
186        let uuid = unwrap_dbus!(uuid, path);
187        let type_ = unwrap_dbus!(type_, path);
188        let devices = unwrap_dbus!(devices, path);
189        let state = NMActiveConnectionState::from_u32(unwrap_dbus!(state, path));
190        let state_flags =
191            NMActivationStateFlags::from_bits_truncate(unwrap_dbus!(state_flags, path));
192        let default = unwrap_dbus!(default, path);
193        let ip4_config = unwrap_dbus!(ip4_config, path);
194        let dhcp4_config = unwrap_dbus!(dhcp4_config, path);
195        let default6 = unwrap_dbus!(default6, path);
196        let ip6_config = unwrap_dbus!(ip6_config, path);
197        let dhcp6_config = unwrap_dbus!(dhcp6_config, path);
198        let vpn = unwrap_dbus!(vpn, path);
199        let controller = unwrap_dbus!(controller, path);
200
201        Ok(Self {
202            connection_path: Property::new(connection_path),
203            specific_object: Property::new(specific_object),
204            id: Property::new(id),
205            uuid: Property::new(uuid),
206            type_: Property::new(type_),
207            devices: Property::new(devices),
208            state: Property::new(state),
209            state_flags: Property::new(state_flags),
210            default: Property::new(default),
211            ip4_config: Property::new(ip4_config),
212            dhcp4_config: Property::new(dhcp4_config),
213            default6: Property::new(default6),
214            ip6_config: Property::new(ip6_config),
215            dhcp6_config: Property::new(dhcp6_config),
216            vpn: Property::new(vpn),
217            controller: Property::new(controller),
218            zbus_connection: connection.clone(),
219            object_path: path,
220            cancellation_token,
221        })
222    }
223
224    /// Emitted when the active connection changes state.
225    ///
226    /// # Errors
227    /// Returns error if D-Bus proxy creation fails.
228    pub async fn active_connection_state_changed_signal(
229        &self,
230    ) -> Result<impl Stream<Item = ActiveConnectionStateChangedEvent>, Error> {
231        let proxy = ConnectionActiveProxy::new(&self.zbus_connection, &self.object_path).await?;
232        let stream = proxy.receive_active_connection_state_changed().await?;
233
234        Ok(stream.filter_map(|signal| async move {
235            signal
236                .args()
237                .ok()
238                .map(|args| ActiveConnectionStateChangedEvent {
239                    state: NMActiveConnectionState::from_u32(args.state),
240                    reason: NMActiveConnectionStateReason::from_u32(args.reason),
241                })
242        }))
243    }
244
245    /// Emitted when the state of the VPN connection has changed.
246    ///
247    /// # Errors
248    /// Returns error if D-Bus proxy creation fails.
249    pub async fn vpn_connection_state_changed_signal(
250        &self,
251    ) -> Result<impl Stream<Item = VpnConnectionStateChangedEvent>, Error> {
252        let proxy = VPNConnectionProxy::new(&self.zbus_connection, &self.object_path).await?;
253        let stream = proxy.receive_vpn_connection_state_changed().await?;
254
255        Ok(stream.filter_map(|signal| async move {
256            signal
257                .args()
258                .ok()
259                .map(|args| VpnConnectionStateChangedEvent {
260                    state: NMVpnConnectionState::from_u32(args.state),
261                    reason: NMVpnConnectionStateReason::from_u32(args.reason),
262                })
263        }))
264    }
265}