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}