Skip to main content

batman_robin/
client.rs

1use super::Error;
2use crate::commands;
3use crate::model;
4use futures::stream::BoxStream;
5use validator::Validate;
6
7/// High-level client for interacting with the BATMAN-adv mesh network.
8///
9/// `Client` provides asynchronous methods to query and configure BATMAN-adv
10/// interfaces and settings via netlink.
11///
12/// Mesh-targeted methods take a [`model::MeshSelector`] by value. You can build
13/// selectors explicitly with [`model::MeshSelector::with_name`] or
14/// [`model::MeshSelector::with_ifindex`].
15///
16/// # Example
17///
18/// ```no_run
19/// use batman_robin::{Client, MeshSelector};
20///
21/// # async fn example() -> Result<(), batman_robin::Error> {
22/// let client = Client::new();
23/// let selector = MeshSelector::with_name("bat0");
24///
25/// let neighbors = client.neighbors(selector.clone()).await?;
26/// println!("{} neighbor entries", neighbors.len());
27/// # Ok(())
28/// # }
29/// ```
30#[derive(Clone)]
31pub struct Client;
32
33impl Default for Client {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl Client {
40    /// Creates a new instance of `Client`.
41    ///
42    /// # Example
43    ///
44    /// ```no_run
45    /// use batman_robin::Client;
46    ///
47    /// let client = Client::new();
48    /// let _ = client;
49    /// ```
50    pub fn new() -> Self {
51        Self {}
52    }
53
54    /// Resolves a `MeshSelector` into a concrete interface index.
55    ///
56    /// This method validates the selector first, then resolves by `ifindex` directly
57    /// when present or by interface `name` via `if_nametoindex`.
58    ///
59    /// # Arguments
60    /// * `selector` - Mesh selector (by name and/or ifindex).
61    ///
62    /// # Errors
63    /// Returns [`Error::Argument`] if validation fails.
64    /// Returns [`Error::Netlink`] if name resolution fails.
65    async fn selector_to_ifindex(&self, selector: model::MeshSelector) -> Result<u32, Error> {
66        selector
67            .validate()
68            .map_err(|err| Error::Argument(err.to_string()))?;
69
70        if let Some(ifindex) = selector.ifindex {
71            return Ok(ifindex);
72        }
73
74        if let Some(name) = selector.name {
75            return commands::if_nametoindex(name.as_str()).await.map_err(|_| {
76                Error::Netlink(format!(
77                    "Error - interface '{}' is not present or not a batman-adv interface",
78                    name
79                ))
80            });
81        }
82
83        Err(Error::Argument("Invalid selector".to_string()))
84    }
85
86    /// Resolves an `InterfaceSelector` into a concrete interface index.
87    ///
88    /// This method validates the selector first, then resolves by `ifindex` directly
89    /// when present or by interface `name` via `if_nametoindex`.
90    ///
91    /// # Arguments
92    /// * `selector` - Interface selector (by name and/or ifindex).
93    ///
94    /// # Errors
95    /// Returns [`Error::Argument`] if validation fails.
96    /// Returns [`Error::Netlink`] if name resolution fails.
97    async fn interface_selector_to_ifindex(
98        &self,
99        selector: model::InterfaceSelector,
100    ) -> Result<u32, Error> {
101        selector
102            .validate()
103            .map_err(|err| Error::Argument(err.to_string()))?;
104
105        if let Some(ifindex) = selector.ifindex {
106            return Ok(ifindex);
107        }
108
109        if let Some(name) = selector.name {
110            return commands::if_nametoindex(name.as_str()).await.map_err(|_| {
111                Error::Netlink(format!("Error - interface '{}' is not present", name))
112            });
113        }
114
115        Err(Error::Argument("Invalid selector".to_string()))
116    }
117
118    /// Retrieves the list of originators for the selected mesh interface.
119    ///
120    /// # Arguments
121    /// * `selector` - Mesh selector.
122    ///
123    /// # Example
124    ///
125    /// ```no_run
126    /// use batman_robin::{Client, MeshSelector};
127    ///
128    /// # async fn example() -> Result<(), batman_robin::Error> {
129    /// let client = Client::new();
130    /// let entries = client.originators(MeshSelector::with_name("bat0")).await?;
131    /// println!("{} originators", entries.len());
132    /// # Ok(())
133    /// # }
134    /// ```
135    #[tracing::instrument(skip(self))]
136    pub async fn originators(
137        &self,
138        selector: model::MeshSelector,
139    ) -> Result<Vec<model::Originator>, Error> {
140        let ifindex = self.selector_to_ifindex(selector).await?;
141        commands::get_originators(ifindex).await
142    }
143
144    /// Retrieves the list of gateways for the selected mesh interface.
145    ///
146    /// # Arguments
147    /// * `selector` - Mesh selector.
148    ///
149    /// # Example
150    ///
151    /// ```no_run
152    /// use batman_robin::{Client, MeshSelector};
153    ///
154    /// # async fn example() -> Result<(), batman_robin::Error> {
155    /// let client = Client::new();
156    /// let gateways = client.gateways(Some(MeshSelector::with_name("bat0"))).await?;
157    /// println!("{} gateways", gateways.len());
158    /// // Pass None to query gateways across all mesh interfaces:
159    /// let all = client.gateways(None).await?;
160    /// # Ok(())
161    /// # }
162    /// ```
163    #[tracing::instrument(skip(self))]
164    pub async fn gateways(
165        &self,
166        selector: Option<model::MeshSelector>,
167    ) -> Result<Vec<model::Gateway>, Error> {
168        let ifindex = if let Some(selector) = selector {
169            Some(self.selector_to_ifindex(selector).await?)
170        } else {
171            None
172        };
173        commands::get_gateways_list(ifindex).await
174    }
175
176    /// Subscribes to gateway change events for the selected mesh interface.
177    ///
178    /// # Arguments
179    /// * `selector` - Mesh selector.
180    ///
181    /// # Example
182    ///
183    /// ```no_run
184    /// use batman_robin::{Client, MeshSelector};
185    /// use futures::StreamExt;
186    ///
187    /// # async fn example() -> Result<(), batman_robin::Error> {
188    /// let client = Client::new();
189    /// let mut events = client
190    ///     .subscribe_gateway_events(Some(MeshSelector::with_name("bat0")))
191    ///     .await?;
192    ///
193    /// while let Some(event) = events.next().await {
194    ///     println!("{:?}", event?);
195    /// }
196    /// # Ok(())
197    /// # }
198    /// ```
199    #[tracing::instrument(skip(self))]
200    pub async fn subscribe_gateway_events(
201        &self,
202        selector: Option<model::MeshSelector>,
203    ) -> Result<BoxStream<'static, Result<model::GatewayEvent, Error>>, Error> {
204        let ifindex = if let Some(selector) = selector {
205            Some(self.selector_to_ifindex(selector).await?)
206        } else {
207            None
208        };
209        commands::UeventListener::subscribe_events(ifindex).await
210    }
211
212    /// Gets current gateway mode and related configuration for the selected mesh interface.
213    ///
214    /// # Arguments
215    /// * `selector` - Mesh selector.
216    ///
217    /// # Example
218    ///
219    /// ```no_run
220    /// use batman_robin::{Client, MeshSelector};
221    ///
222    /// # async fn example() -> Result<(), batman_robin::Error> {
223    /// let client = Client::new();
224    /// let gw = client.get_gw_mode(MeshSelector::with_name("bat0")).await?;
225    /// println!("mode={:?}", gw.mode);
226    /// # Ok(())
227    /// # }
228    /// ```
229    #[tracing::instrument(skip(self))]
230    pub async fn get_gw_mode(
231        &self,
232        selector: model::MeshSelector,
233    ) -> Result<model::GatewayInfo, Error> {
234        let ifindex = self.selector_to_ifindex(selector).await?;
235        commands::get_gateway(ifindex).await
236    }
237
238    /// Sets gateway mode and optional parameters for the selected mesh interface.
239    ///
240    /// # Arguments
241    /// * `selector` - Mesh selector.
242    /// * `mode` - Gateway mode to apply.
243    /// * `down` - Optional downstream bandwidth parameter.
244    /// * `up` - Optional upstream bandwidth parameter.
245    /// * `sel_class` - Optional gateway selection class.
246    ///
247    /// # Errors
248    /// Returns [`Error`] if selector validation, selector resolution, or netlink write fails.
249    ///
250    /// # Example
251    ///
252    /// ```no_run
253    /// use batman_robin::{Client, GwMode, MeshSelector};
254    ///
255    /// # async fn example() -> Result<(), batman_robin::Error> {
256    /// let client = Client::new();
257    /// client
258    ///     .set_gw_mode(
259    ///         MeshSelector::with_name("bat0"),
260    ///         GwMode::Client,
261    ///         None,
262    ///         None,
263    ///         Some(20),
264    ///     )
265    ///     .await?;
266    /// # Ok(())
267    /// # }
268    /// ```
269    #[tracing::instrument(skip(self))]
270    pub async fn set_gw_mode(
271        &self,
272        selector: model::MeshSelector,
273        mode: model::GwMode,
274        down: Option<u32>,
275        up: Option<u32>,
276        sel_class: Option<u32>,
277    ) -> Result<(), Error> {
278        let ifindex = self.selector_to_ifindex(selector).await?;
279        commands::set_gateway(mode, down, up, sel_class, ifindex).await
280    }
281
282    /// Retrieves global translation table entries for the selected mesh interface.
283    ///
284    /// # Arguments
285    /// * `selector` - Mesh selector.
286    ///
287    /// # Example
288    ///
289    /// ```no_run
290    /// use batman_robin::{Client, MeshSelector};
291    ///
292    /// # async fn example() -> Result<(), batman_robin::Error> {
293    /// let client = Client::new();
294    /// let tg = client.transglobal(MeshSelector::with_name("bat0")).await?;
295    /// println!("{} global entries", tg.len());
296    /// # Ok(())
297    /// # }
298    /// ```
299    pub async fn transglobal(
300        &self,
301        selector: model::MeshSelector,
302    ) -> Result<Vec<model::TransglobalEntry>, Error> {
303        let ifindex = self.selector_to_ifindex(selector).await?;
304        commands::get_transglobal(ifindex).await
305    }
306
307    /// Retrieves local translation table entries for the selected mesh interface.
308    ///
309    /// # Arguments
310    /// * `selector` - Mesh selector.
311    ///
312    /// # Example
313    ///
314    /// ```no_run
315    /// use batman_robin::{Client, MeshSelector};
316    ///
317    /// # async fn example() -> Result<(), batman_robin::Error> {
318    /// let client = Client::new();
319    /// let tl = client.translocal(MeshSelector::with_name("bat0")).await?;
320    /// println!("{} local entries", tl.len());
321    /// # Ok(())
322    /// # }
323    /// ```
324    #[tracing::instrument(skip(self))]
325    pub async fn translocal(
326        &self,
327        selector: model::MeshSelector,
328    ) -> Result<Vec<model::TranslocalEntry>, Error> {
329        let ifindex = self.selector_to_ifindex(selector).await?;
330        commands::get_translocal(ifindex).await
331    }
332
333    /// Retrieves the list of neighbors for the selected mesh interface.
334    ///
335    /// # Arguments
336    /// * `selector` - Mesh selector.
337    ///
338    /// # Example
339    ///
340    /// ```no_run
341    /// use batman_robin::{Client, MeshSelector};
342    ///
343    /// # async fn example() -> Result<(), batman_robin::Error> {
344    /// let client = Client::new();
345    /// let neighbors = client.neighbors(MeshSelector::with_name("bat0")).await?;
346    /// println!("{} neighbors", neighbors.len());
347    /// # Ok(())
348    /// # }
349    /// ```
350    #[tracing::instrument(skip(self))]
351    pub async fn neighbors(
352        &self,
353        selector: model::MeshSelector,
354    ) -> Result<Vec<model::Neighbor>, Error> {
355        let ifindex = self.selector_to_ifindex(selector).await?;
356        commands::get_neighbors(ifindex).await
357    }
358
359    /// Retrieves the list of physical interfaces attached to the selected mesh interface.
360    ///
361    /// # Arguments
362    /// * `selector` - Mesh selector.
363    ///
364    /// # Example
365    ///
366    /// ```no_run
367    /// use batman_robin::{Client, MeshSelector};
368    ///
369    /// # async fn example() -> Result<(), batman_robin::Error> {
370    /// let client = Client::new();
371    /// let ifaces = client.interface_list(MeshSelector::with_name("bat0")).await?;
372    /// println!("{} attached interfaces", ifaces.len());
373    /// # Ok(())
374    /// # }
375    /// ```
376    #[tracing::instrument(skip(self))]
377    pub async fn interface_list(
378        &self,
379        selector: model::MeshSelector,
380    ) -> Result<Vec<model::Interface>, Error> {
381        let ifindex = self.selector_to_ifindex(selector).await?;
382        commands::get_interfaces(ifindex).await
383    }
384
385    /// Adds a physical interface to a selected mesh interface.
386    ///
387    /// # Arguments
388    /// * `selector` - Mesh selector identifying the target mesh interface.
389    /// * `interface_selector` - Interface selector for the physical interface to add.
390    ///
391    /// # Example
392    ///
393    /// ```no_run
394    /// use batman_robin::{Client, InterfaceSelector, MeshSelector};
395    ///
396    /// # async fn example() -> Result<(), batman_robin::Error> {
397    /// let client = Client::new();
398    /// client
399    ///     .interface_add(
400    ///         MeshSelector::with_name("bat0"),
401    ///         InterfaceSelector::with_name("wlan0"),
402    ///     )
403    ///     .await?;
404    /// # Ok(())
405    /// # }
406    /// ```
407    pub async fn interface_add(
408        &self,
409        selector: model::MeshSelector,
410        interface_selector: model::InterfaceSelector,
411    ) -> Result<(), Error> {
412        let mesh_ifindex = self.selector_to_ifindex(selector).await?;
413        let iface_ifindex = self
414            .interface_selector_to_ifindex(interface_selector)
415            .await?;
416
417        commands::set_interface(iface_ifindex, Some(mesh_ifindex)).await
418    }
419
420    /// Removes a physical interface from any mesh interface.
421    ///
422    /// # Arguments
423    /// * `interface_selector` - Interface selector for the physical interface to remove.
424    ///
425    /// # Example
426    ///
427    /// ```no_run
428    /// use batman_robin::{Client, InterfaceSelector};
429    ///
430    /// # async fn example() -> Result<(), batman_robin::Error> {
431    /// let client = Client::new();
432    /// client
433    ///     .interface_remove(InterfaceSelector::with_name("wlan0"))
434    ///     .await?;
435    /// # Ok(())
436    /// # }
437    /// ```
438    #[tracing::instrument(skip(self))]
439    pub async fn interface_remove(
440        &self,
441        interface_selector: model::InterfaceSelector,
442    ) -> Result<(), Error> {
443        let iface_ifindex = self
444            .interface_selector_to_ifindex(interface_selector)
445            .await?;
446
447        commands::set_interface(iface_ifindex, None).await
448    }
449
450    /// Creates a new BATMAN-adv mesh interface with an optional routing algorithm and MAC address.
451    ///
452    /// # Arguments
453    /// * `mesh_if` - Name of the interface to create.
454    /// * `routing_algo` - Optional routing algorithm string.
455    /// * `mac_addr` - Optional hardware address. A random locally-administered address is
456    ///   generated if `None` to avoid conflicts with other devices on the network.
457    ///
458    /// # Example
459    ///
460    /// ```no_run
461    /// use batman_robin::Client;
462    ///
463    /// # async fn example() -> Result<(), batman_robin::Error> {
464    /// let client = Client::new();
465    /// client.mesh_create("bat0", Some("BATMAN_V"), None).await?;
466    /// # Ok(())
467    /// # }
468    /// ```
469    #[tracing::instrument(skip(self))]
470    pub async fn mesh_create(
471        &self,
472        mesh_if: &str,
473        routing_algo: Option<&str>,
474        mac_addr: Option<macaddr::MacAddr6>,
475    ) -> Result<(), Error> {
476        commands::create_mesh(mesh_if, routing_algo, mac_addr).await
477    }
478
479    /// Lists BATMAN-adv mesh interfaces available on the host.
480    ///
481    /// This method returns interfaces whose kernel link kind is `batadv`.
482    /// It is useful to discover existing mesh interfaces before selecting one
483    /// for operations like neighbors, gateways, or interface management.
484    ///
485    /// # Returns
486    ///
487    /// Returns a vector of mesh interface names for every detected
488    /// BATMAN-adv mesh interface.
489    ///
490    /// # Example
491    ///
492    /// ```no_run
493    /// use batman_robin::Client;
494    ///
495    /// # async fn example() -> Result<(), batman_robin::Error> {
496    /// let client = Client::new();
497    /// let meshes = client.mesh_list().await?;
498    ///
499    /// for mesh in meshes {
500    ///     println!("mesh={}", mesh);
501    /// }
502    /// # Ok(())
503    /// # }
504    /// ```
505    #[tracing::instrument(skip(self))]
506    pub async fn mesh_list(&self) -> Result<Vec<String>, Error> {
507        commands::list_meshes().await
508    }
509
510    /// Destroys a BATMAN-adv mesh interface selected by name or ifindex.
511    ///
512    /// # Arguments
513    /// * `selector` - Mesh selector.
514    ///
515    /// # Example
516    ///
517    /// ```no_run
518    /// use batman_robin::{Client, MeshSelector};
519    ///
520    /// # async fn example() -> Result<(), batman_robin::Error> {
521    /// let client = Client::new();
522    /// client.mesh_delete(MeshSelector::with_name("bat0")).await?;
523    /// # Ok(())
524    /// # }
525    /// ```
526    #[tracing::instrument(skip(self))]
527    pub async fn mesh_delete(&self, selector: model::MeshSelector) -> Result<(), Error> {
528        let ifindex = self.selector_to_ifindex(selector).await?;
529        commands::delete_mesh(ifindex).await
530    }
531
532    /// Counts the number of physical interfaces attached to the selected mesh interface.
533    ///
534    /// # Arguments
535    /// * `selector` - Mesh selector.
536    ///
537    /// # Example
538    ///
539    /// ```no_run
540    /// use batman_robin::{Client, MeshSelector};
541    ///
542    /// # async fn example() -> Result<(), batman_robin::Error> {
543    /// let client = Client::new();
544    /// let count = client.interfaces_count(MeshSelector::with_name("bat0")).await?;
545    /// println!("{count}");
546    /// # Ok(())
547    /// # }
548    /// ```
549    #[tracing::instrument(skip(self))]
550    pub async fn interfaces_count(&self, selector: model::MeshSelector) -> Result<u32, Error> {
551        let ifindex = self.selector_to_ifindex(selector).await?;
552        commands::count_interfaces(ifindex).await
553    }
554
555    /// Gets whether packet aggregation is enabled on the selected mesh interface.
556    ///
557    /// # Arguments
558    /// * `selector` - Mesh selector.
559    ///
560    /// # Example
561    ///
562    /// ```no_run
563    /// use batman_robin::{Client, MeshSelector};
564    ///
565    /// # async fn example() -> Result<(), batman_robin::Error> {
566    /// let client = Client::new();
567    /// let enabled = client.get_aggregation(MeshSelector::with_name("bat0")).await?;
568    /// println!("{enabled}");
569    /// # Ok(())
570    /// # }
571    /// ```
572    #[tracing::instrument(skip(self))]
573    pub async fn get_aggregation(&self, selector: model::MeshSelector) -> Result<bool, Error> {
574        let ifindex = self.selector_to_ifindex(selector).await?;
575        commands::get_aggregation(ifindex).await
576    }
577
578    /// Enables or disables packet aggregation on the selected mesh interface.
579    ///
580    /// # Arguments
581    /// * `selector` - Mesh selector.
582    /// * `val` - `true` to enable, `false` to disable.
583    ///
584    /// # Example
585    ///
586    /// ```no_run
587    /// use batman_robin::{Client, MeshSelector};
588    ///
589    /// # async fn example() -> Result<(), batman_robin::Error> {
590    /// let client = Client::new();
591    /// client
592    ///     .set_aggregation(MeshSelector::with_name("bat0"), true)
593    ///     .await?;
594    /// # Ok(())
595    /// # }
596    /// ```
597    #[tracing::instrument(skip(self))]
598    pub async fn set_aggregation(
599        &self,
600        selector: model::MeshSelector,
601        val: bool,
602    ) -> Result<(), Error> {
603        let ifindex = self.selector_to_ifindex(selector).await?;
604        commands::set_aggregation(ifindex, val).await
605    }
606
607    /// Gets whether AP isolation is enabled on the selected mesh interface.
608    ///
609    /// # Arguments
610    /// * `selector` - Mesh selector.
611    ///
612    /// # Example
613    ///
614    /// ```no_run
615    /// use batman_robin::{Client, MeshSelector};
616    ///
617    /// # async fn example() -> Result<(), batman_robin::Error> {
618    /// let client = Client::new();
619    /// let enabled = client
620    ///     .get_ap_isolation(MeshSelector::with_name("bat0"))
621    ///     .await?;
622    /// println!("{enabled}");
623    /// # Ok(())
624    /// # }
625    /// ```
626    #[tracing::instrument(skip(self))]
627    pub async fn get_ap_isolation(&self, selector: model::MeshSelector) -> Result<bool, Error> {
628        let ifindex = self.selector_to_ifindex(selector).await?;
629        commands::get_ap_isolation(ifindex).await
630    }
631
632    /// Enables or disables AP isolation on the selected mesh interface.
633    ///
634    /// # Arguments
635    /// * `selector` - Mesh selector.
636    /// * `val` - `true` to enable, `false` to disable.
637    ///
638    /// # Example
639    ///
640    /// ```no_run
641    /// use batman_robin::{Client, MeshSelector};
642    ///
643    /// # async fn example() -> Result<(), batman_robin::Error> {
644    /// let client = Client::new();
645    /// client
646    ///     .set_ap_isolation(MeshSelector::with_name("bat0"), true)
647    ///     .await?;
648    /// # Ok(())
649    /// # }
650    /// ```
651    #[tracing::instrument(skip(self))]
652    pub async fn set_ap_isolation(
653        &self,
654        selector: model::MeshSelector,
655        val: bool,
656    ) -> Result<(), Error> {
657        let ifindex = self.selector_to_ifindex(selector).await?;
658        commands::set_ap_isolation(ifindex, val).await
659    }
660
661    /// Gets whether bridge loop avoidance is enabled on the selected mesh interface.
662    ///
663    /// # Arguments
664    /// * `selector` - Mesh selector.
665    ///
666    /// # Example
667    ///
668    /// ```no_run
669    /// use batman_robin::{Client, MeshSelector};
670    ///
671    /// # async fn example() -> Result<(), batman_robin::Error> {
672    /// let client = Client::new();
673    /// let enabled = client
674    ///     .get_bridge_loop_avoidance(MeshSelector::with_name("bat0"))
675    ///     .await?;
676    /// println!("{enabled}");
677    /// # Ok(())
678    /// # }
679    /// ```
680    #[tracing::instrument(skip(self))]
681    pub async fn get_bridge_loop_avoidance(
682        &self,
683        selector: model::MeshSelector,
684    ) -> Result<bool, Error> {
685        let ifindex = self.selector_to_ifindex(selector).await?;
686        commands::get_bridge_loop_avoidance(ifindex).await
687    }
688
689    /// Enables or disables bridge loop avoidance on the selected mesh interface.
690    ///
691    /// # Arguments
692    /// * `selector` - Mesh selector.
693    /// * `val` - `true` to enable, `false` to disable.
694    ///
695    /// # Example
696    ///
697    /// ```no_run
698    /// use batman_robin::{Client, MeshSelector};
699    ///
700    /// # async fn example() -> Result<(), batman_robin::Error> {
701    /// let client = Client::new();
702    /// client
703    ///     .set_bridge_loop_avoidance(MeshSelector::with_name("bat0"), true)
704    ///     .await?;
705    /// # Ok(())
706    /// # }
707    /// ```
708    #[tracing::instrument(skip(self))]
709    pub async fn set_bridge_loop_avoidance(
710        &self,
711        selector: model::MeshSelector,
712        val: bool,
713    ) -> Result<(), Error> {
714        let ifindex = self.selector_to_ifindex(selector).await?;
715        commands::set_bridge_loop_avoidance(ifindex, val).await
716    }
717
718    /// Retrieves the system default routing algorithm for BATMAN-adv.
719    ///
720    /// # Errors
721    /// Returns [`Error`] if the value cannot be retrieved from kernel state.
722    ///
723    /// # Example
724    ///
725    /// ```no_run
726    /// use batman_robin::Client;
727    ///
728    /// # async fn example() -> Result<(), batman_robin::Error> {
729    /// let client = Client::new();
730    /// let algo = client.get_default_routing_algo().await?;
731    /// println!("{algo}");
732    /// # Ok(())
733    /// # }
734    /// ```
735    #[tracing::instrument(skip(self))]
736    pub async fn get_default_routing_algo(&self) -> Result<String, Error> {
737        commands::get_default_routing_algo().await
738    }
739
740    /// Retrieves all active routing algorithms currently in use and their interfaces.
741    ///
742    /// Returns a vector of `(interface_name, algorithm_name)`.
743    ///
744    /// # Example
745    ///
746    /// ```no_run
747    /// use batman_robin::Client;
748    ///
749    /// # async fn example() -> Result<(), batman_robin::Error> {
750    /// let client = Client::new();
751    /// let active = client.get_active_routing_algos().await?;
752    /// for (iface, algo) in active {
753    ///     println!("{iface}: {algo}");
754    /// }
755    /// # Ok(())
756    /// # }
757    /// ```
758    #[tracing::instrument(skip(self))]
759    pub async fn get_active_routing_algos(&self) -> Result<Vec<(String, String)>, Error> {
760        commands::get_active_routing_algos().await
761    }
762
763    /// Retrieves all routing algorithms available on the system.
764    ///
765    /// # Example
766    ///
767    /// ```no_run
768    /// use batman_robin::Client;
769    ///
770    /// # async fn example() -> Result<(), batman_robin::Error> {
771    /// let client = Client::new();
772    /// let available = client.get_available_routing_algos().await?;
773    /// println!("{} available algos", available.len());
774    /// # Ok(())
775    /// # }
776    /// ```
777    #[tracing::instrument(skip(self))]
778    pub async fn get_available_routing_algos(&self) -> Result<Vec<String>, Error> {
779        commands::get_available_routing_algos().await
780    }
781
782    /// Sets the system default routing algorithm.
783    ///
784    /// # Arguments
785    /// * `algo` - Algorithm name to set as default.
786    ///
787    /// # Example
788    ///
789    /// ```no_run
790    /// use batman_robin::Client;
791    ///
792    /// # async fn example() -> Result<(), batman_robin::Error> {
793    /// let client = Client::new();
794    /// client.set_default_routing_algo("BATMAN_V").await?;
795    /// # Ok(())
796    /// # }
797    /// ```
798    #[tracing::instrument(skip(self))]
799    pub async fn set_default_routing_algo(&self, algo: &str) -> Result<(), Error> {
800        commands::set_default_routing_algo(algo).await
801    }
802}