ftth_rtnl/
link.rs

1#![allow(unreachable_patterns)]
2
3use ftth_common::channel::{AsyncWorldClient, AsyncWorldServer};
4
5use futures::TryStreamExt;
6
7use std::fmt::{Debug, Display};
8
9pub(crate) type Client = AsyncWorldClient<RtnlLinkRequest, RtnlLinkResponse>;
10pub(crate) type Server = AsyncWorldServer<RtnlLinkRequest, RtnlLinkResponse>;
11
12#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub struct MacAddr {
14    pub inner: [u8; 6],
15}
16
17impl MacAddr {
18    pub const fn new(inner: [u8; 6]) -> Self {
19        Self {
20            inner,
21        }
22    }
23}
24
25impl Default for MacAddr {
26    fn default() -> Self {
27        Self {
28            inner: [0; 6],
29        }
30    }
31}
32
33impl Debug for MacAddr {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        f.write_str(&format!("MacAddr({})", self))
36    }
37}
38
39impl Display for MacAddr {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        f.write_str(&format!(
42            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
43            self.inner[0],
44            self.inner[1],
45            self.inner[2],
46            self.inner[3],
47            self.inner[4],
48            self.inner[5],
49        ))
50    }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct Interface {
55    pub if_name: String,
56    pub if_id: u32,
57}
58
59#[derive(Debug, Clone, PartialEq)]
60#[non_exhaustive]
61pub enum RtnlLinkRequest {
62    InterfaceList,
63    InterfaceGet {
64        if_id: u32,
65    },
66    InterfaceGetByName {
67        if_name: String,
68    },
69    MacAddrGet {
70        if_id: u32,
71    },
72    MacAddrSet {
73        if_id: u32,
74        mac_addr: MacAddr,
75    },
76}
77
78#[derive(Debug, Clone, PartialEq)]
79#[non_exhaustive]
80pub enum RtnlLinkResponse {
81    Success,
82    Failed,
83    NotImplemented,
84    NotFound,
85    InterfaceList(Vec<Interface>),
86    Interface(Interface),
87    MacAddr(MacAddr),
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Hash)]
91pub struct RtnlLinkClient {
92    client: Client,
93}
94
95impl RtnlLinkClient {
96    pub(crate) fn new(client: Client) -> Self {
97        Self {
98            client,
99        }
100    }
101
102    pub fn interface_get_by_name(&self, name: &str) -> std::io::Result<Interface> {
103        let name = name.to_owned();
104        let res = self.client.send_request(RtnlLinkRequest::InterfaceGetByName { if_name: name })?;
105        match res {
106            RtnlLinkResponse::Interface(interface) => {
107                return Ok(interface);
108            },
109            _ => {},
110        }
111        Err(std::io::Error::other("Not found"))
112    }
113
114    pub fn mac_addr_get(&self, if_id: u32) -> std::io::Result<Option<MacAddr>> {
115        let res = self.client.send_request(RtnlLinkRequest::MacAddrGet { if_id })?;
116        match res {
117            RtnlLinkResponse::MacAddr(addr) => {
118                return Ok(Some(addr));
119            },
120            _ => {},
121        }
122        Ok(None)
123    }
124
125    pub fn interface_list(&self) -> std::io::Result<Vec<Interface>> {
126        let res = self.client.send_request(RtnlLinkRequest::InterfaceList)?;
127        match res {
128            RtnlLinkResponse::InterfaceList(list) => {
129                return Ok(list);
130            },
131            _ => {},
132        }
133        Err(std::io::Error::other("Unknown error"))
134    }
135}
136
137pub(crate) async fn run_server(mut server: Server, mut handle: rtnetlink::LinkHandle) {
138    'reqloop: while let Some((req, respond)) = server.accept().await {
139        match req {
140            RtnlLinkRequest::InterfaceGetByName { if_name } => {
141                let response = handle.get().match_name(if_name.to_owned()).execute();
142                futures::pin_mut!(response);
143                while let Ok(Some(response)) = response.try_next().await {
144                    let if_index = response.header.index;
145                    if if_index == 0 {
146                        continue;
147                    }
148
149                    respond(RtnlLinkResponse::Interface(Interface { if_id: if_index, if_name: if_name.to_owned() }));
150                    continue 'reqloop;
151                }
152                respond(RtnlLinkResponse::NotFound);
153            },
154            RtnlLinkRequest::MacAddrGet { if_id } => {
155                let if_index = if_id;
156                if if_index == 0 {
157                    respond(RtnlLinkResponse::NotFound);
158                    continue 'reqloop;
159                }
160                let response = handle.get().match_index(if_index).execute();
161                futures::pin_mut!(response);
162                while let Ok(Some(response)) = response.try_next().await {
163                    for link in response.attributes.iter() {
164                        match link {
165                            netlink_packet_route::link::LinkAttribute::Address(addr) => {
166                                respond(RtnlLinkResponse::MacAddr(MacAddr::new(addr[0..6].try_into().unwrap_or([0; 6]))));
167                                continue 'reqloop;
168                            }
169                            _ => {}
170                        }
171                    }
172                }
173                respond(RtnlLinkResponse::NotFound);
174            },
175            RtnlLinkRequest::InterfaceList => {
176                let mut interfaces = Vec::new();
177                let response = handle.get().execute();
178                futures::pin_mut!(response);
179                while let Ok(Some(response)) = response.try_next().await {
180                    let if_index = response.header.index;
181                    let mut if_name = None;
182                    for link in response.attributes.iter() {
183                        match link {
184                            netlink_packet_route::link::LinkAttribute::IfName(name) => {
185                                if_name = Some(name.clone());
186                            }
187                            _ => {}
188                        }
189                    }
190
191                    if if_index == 0 || if_name.is_none() {
192                        continue;
193                    }
194
195                    interfaces.push(Interface { if_id: if_index, if_name: if_name.unwrap() });
196                }
197                respond(RtnlLinkResponse::InterfaceList(interfaces));
198            }
199            _ => respond(RtnlLinkResponse::NotImplemented),
200        }
201    }
202}