Skip to main content

batman_robin/model/
gateway_event.rs

1use macaddr::MacAddr;
2use std::time::SystemTime;
3
4/// Gateway change event emitted by batman-adv kernel module.
5///
6/// When a batman-adv node is in gateway client mode, it emits uevents
7/// when the selected gateway changes. Applications can subscribe to these
8/// events to perform actions like starting/renewing DHCP leases.
9///
10/// # Examples
11///
12/// ```ignore
13/// let client = batman_robin::Client::new();
14/// let mut events = client
15///     .subscribe_gateway_events(batman_robin::MeshSelector::with_name("bat0"))
16///     .await?;
17///
18/// while let Some(event) = events.next().await {
19///     match event?.action {
20///         GatewayEventAction::Add => println!("Gateway selected: {:?}", event?.gateway_mac),
21///         GatewayEventAction::Change => println!("Better gateway found"),
22///         GatewayEventAction::Delete => println!("Gateway lost"),
23///     }
24/// }
25/// ```
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct GatewayEvent {
28    /// Timestamp when the event was received
29    pub timestamp: SystemTime,
30
31    /// Mesh interface index (e.g. from `if_nametoindex("bat0")`)
32    pub meshif: u32,
33
34    /// Mesh interface name (e.g. "bat0")
35    pub meshif_name: String,
36
37    /// Type of gateway change event
38    pub action: GatewayEventAction,
39
40    /// MAC address of the selected gateway (present for ADD/CHANGE, absent for DELETE)
41    pub gateway_mac: Option<MacAddr>,
42}
43
44/// Gateway event action types
45///
46/// These correspond to batman-adv's BATACTION uevent field.
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48#[repr(u8)]
49pub enum GatewayEventAction {
50    /// First gateway was selected (ADD)
51    ///
52    /// This event is sent when a batman-adv node transitions to having a selected
53    /// gateway. Applications should typically start a DHCP client on this event.
54    Add = 1,
55
56    /// A better gateway was found (CHANGE)
57    ///
58    /// This event is sent when a batman-adv node switches to a different gateway
59    /// due to improved link quality or other selection criteria.
60    /// Applications should typically renew their DHCP lease on this event.
61    Change = 2,
62
63    /// The selected gateway is no longer available (DEL)
64    ///
65    /// This event is sent when the currently selected gateway disappears
66    /// and no alternative gateway is available. The `gateway_mac` field
67    /// will be `None` for this action.
68    Delete = 3,
69}
70
71impl GatewayEvent {
72    /// Create a new gateway event
73    pub fn new(
74        meshif: u32,
75        meshif_name: String,
76        action: GatewayEventAction,
77        gateway_mac: Option<MacAddr>,
78    ) -> Self {
79        Self {
80            timestamp: SystemTime::now(),
81            meshif,
82            meshif_name,
83            action,
84            gateway_mac,
85        }
86    }
87
88    /// Check if this event has a valid associated gateway MAC
89    pub fn has_gateway(&self) -> bool {
90        self.gateway_mac.is_some()
91    }
92}
93
94impl std::fmt::Display for GatewayEventAction {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        match self {
97            Self::Add => write!(f, "ADD"),
98            Self::Change => write!(f, "CHANGE"),
99            Self::Delete => write!(f, "DELETE"),
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_gateway_event_creation() {
110        let mac = "60:09:c3:aa:bb:cc".parse().unwrap();
111        let event = GatewayEvent::new(6, "bat0".to_string(), GatewayEventAction::Add, Some(mac));
112
113        assert_eq!(event.meshif, 6);
114        assert_eq!(event.meshif_name, "bat0");
115        assert_eq!(event.action, GatewayEventAction::Add);
116        assert_eq!(event.gateway_mac, Some(mac));
117        assert!(event.has_gateway());
118    }
119
120    #[test]
121    fn test_delete_event_no_gateway() {
122        let event = GatewayEvent::new(6, "bat0".to_string(), GatewayEventAction::Delete, None);
123
124        assert_eq!(event.action, GatewayEventAction::Delete);
125        assert!(!event.has_gateway());
126    }
127
128    #[test]
129    fn test_action_display() {
130        assert_eq!(GatewayEventAction::Add.to_string(), "ADD");
131        assert_eq!(GatewayEventAction::Change.to_string(), "CHANGE");
132        assert_eq!(GatewayEventAction::Delete.to_string(), "DELETE");
133    }
134}