1#![allow(unreachable_patterns)]
2
3use ftth_common::channel::{AsyncWorldClient, AsyncWorldServer};
4
5use futures::TryStreamExt;
6
7use std::fmt::{Debug, Display};
8use std::io::{self, ErrorKind};
9
10use netlink_packet_route::link::LinkFlags;
11use rtnetlink::{LinkMessageBuilder, LinkUnspec};
12
13pub(crate) type Client = AsyncWorldClient<RtnlLinkRequest, RtnlLinkResponse>;
14pub(crate) type Server = AsyncWorldServer<RtnlLinkRequest, RtnlLinkResponse>;
15
16#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17pub struct MacAddr {
18 pub inner: [u8; 6],
19}
20
21impl MacAddr {
22 pub const fn new(inner: [u8; 6]) -> Self {
23 Self { inner }
24 }
25}
26
27impl Default for MacAddr {
28 fn default() -> Self {
29 Self { inner: [0; 6] }
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 { if_id: u32 },
64 InterfaceGetByName { if_name: String },
65 MacAddrGet { if_id: u32 },
66 MacAddrSet { if_id: u32, mac_addr: MacAddr },
67 MtuGet { if_id: u32 },
68 InterfaceSetAdmin { if_id: u32, up: bool },
69 InterfaceSetPromisc { if_id: u32, enable: bool },
70 InterfaceSetArp { if_id: u32, enable: bool },
71 InterfaceSetMtu { if_id: u32, mtu: u32 },
72 InterfaceRename { if_id: u32, if_name: String },
73 InterfaceSetAllMulticast { if_id: u32, enable: bool },
74}
75
76#[derive(Debug, Clone, PartialEq)]
77#[non_exhaustive]
78pub enum RtnlLinkResponse {
79 Success,
80 Failed,
81 NotImplemented,
82 NotFound,
83 InterfaceList(Vec<Interface>),
84 Interface(Interface),
85 MacAddr(MacAddr),
86 Mtu(u32),
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Hash)]
90pub struct RtnlLinkClient {
91 client: Client,
92}
93
94impl RtnlLinkClient {
95 pub(crate) fn new(client: Client) -> Self {
96 Self { client }
97 }
98
99 pub fn interface_set_up(&self, if_id: u32) -> io::Result<()> {
100 self.interface_set_admin_state(if_id, true)
101 }
102
103 pub fn interface_set_down(&self, if_id: u32) -> io::Result<()> {
104 self.interface_set_admin_state(if_id, false)
105 }
106
107 pub fn interface_set_admin_state(&self, if_id: u32, up: bool) -> io::Result<()> {
108 let res = self
109 .client
110 .send_request(RtnlLinkRequest::InterfaceSetAdmin { if_id, up })?;
111 let op = if up {
112 "Set interface up"
113 } else {
114 "Set interface down"
115 };
116 handle_status_response(op, res)
117 }
118
119 pub fn interface_set_promiscuous(&self, if_id: u32, enable: bool) -> io::Result<()> {
120 let res = self
121 .client
122 .send_request(RtnlLinkRequest::InterfaceSetPromisc { if_id, enable })?;
123 handle_status_response(
124 if enable {
125 "Enable promiscuous mode"
126 } else {
127 "Disable promiscuous mode"
128 },
129 res,
130 )
131 }
132
133 pub fn interface_set_arp(&self, if_id: u32, enable: bool) -> io::Result<()> {
134 let res = self
135 .client
136 .send_request(RtnlLinkRequest::InterfaceSetArp { if_id, enable })?;
137 handle_status_response(if enable { "Enable ARP" } else { "Disable ARP" }, res)
138 }
139
140 pub fn interface_set_mtu(&self, if_id: u32, mtu: u32) -> io::Result<()> {
141 let res = self
142 .client
143 .send_request(RtnlLinkRequest::InterfaceSetMtu { if_id, mtu })?;
144 handle_status_response("Set MTU", res)
145 }
146
147 pub fn interface_rename(&self, if_id: u32, new_name: &str) -> io::Result<()> {
148 let res = self.client.send_request(RtnlLinkRequest::InterfaceRename {
149 if_id,
150 if_name: new_name.to_owned(),
151 })?;
152 handle_status_response("Rename interface", res)
153 }
154
155 pub fn interface_get(&self, if_id: u32) -> io::Result<Interface> {
156 let res = self
157 .client
158 .send_request(RtnlLinkRequest::InterfaceGet { if_id })?;
159 match res {
160 RtnlLinkResponse::Interface(interface) => Ok(interface),
161 RtnlLinkResponse::NotFound => {
162 Err(io::Error::new(ErrorKind::NotFound, "Interface not found"))
163 }
164 _ => Err(io::Error::other("Failed to get interface")),
165 }
166 }
167
168 pub fn interface_get_by_name(&self, name: &str) -> std::io::Result<Interface> {
169 let name = name.to_owned();
170 let res = self
171 .client
172 .send_request(RtnlLinkRequest::InterfaceGetByName { if_name: name })?;
173 match res {
174 RtnlLinkResponse::Interface(interface) => {
175 return Ok(interface);
176 }
177 _ => {}
178 }
179 Err(std::io::Error::other("Not found"))
180 }
181
182 pub fn mac_addr_get(&self, if_id: u32) -> std::io::Result<Option<MacAddr>> {
183 let res = self
184 .client
185 .send_request(RtnlLinkRequest::MacAddrGet { if_id })?;
186 match res {
187 RtnlLinkResponse::MacAddr(addr) => {
188 return Ok(Some(addr));
189 }
190 _ => {}
191 }
192 Ok(None)
193 }
194
195 pub fn mtu_get(&self, if_id: u32) -> io::Result<u32> {
196 let res = self
197 .client
198 .send_request(RtnlLinkRequest::MtuGet { if_id })?;
199 match res {
200 RtnlLinkResponse::Mtu(mtu) => Ok(mtu),
201 RtnlLinkResponse::NotFound => {
202 Err(io::Error::new(ErrorKind::NotFound, "Interface not found"))
203 }
204 _ => Err(io::Error::other("Failed to get MTU")),
205 }
206 }
207
208 pub fn mac_addr_set(&self, if_id: u32, mac_addr: MacAddr) -> io::Result<()> {
209 let res = self
210 .client
211 .send_request(RtnlLinkRequest::MacAddrSet { if_id, mac_addr })?;
212 handle_status_response("Set MAC address", res)
213 }
214
215 pub fn interface_set_all_multicast(&self, if_id: u32, enable: bool) -> io::Result<()> {
216 let res = self
217 .client
218 .send_request(RtnlLinkRequest::InterfaceSetAllMulticast { if_id, enable })?;
219 handle_status_response(
220 if enable {
221 "Enable all-multicast"
222 } else {
223 "Disable all-multicast"
224 },
225 res,
226 )
227 }
228
229 pub fn interface_list(&self) -> std::io::Result<Vec<Interface>> {
230 let res = self.client.send_request(RtnlLinkRequest::InterfaceList)?;
231 match res {
232 RtnlLinkResponse::InterfaceList(list) => {
233 return Ok(list);
234 }
235 _ => {}
236 }
237 Err(std::io::Error::other("Unknown error"))
238 }
239}
240
241fn handle_status_response(op: &str, response: RtnlLinkResponse) -> io::Result<()> {
242 match response {
243 RtnlLinkResponse::Success => Ok(()),
244 RtnlLinkResponse::NotFound => Err(io::Error::new(
245 ErrorKind::NotFound,
246 format!("{}: interface not found", op),
247 )),
248 RtnlLinkResponse::Failed => Err(io::Error::other(format!("{} failed", op))),
249 RtnlLinkResponse::NotImplemented => Err(io::Error::new(
250 ErrorKind::Unsupported,
251 format!("{} not implemented", op),
252 )),
253 other => Err(io::Error::other(format!(
254 "{} returned unexpected response: {:?}",
255 op, other
256 ))),
257 }
258}
259
260async fn apply_link_set<F>(
261 handle: &rtnetlink::LinkHandle,
262 if_id: u32,
263 op: F,
264) -> Result<(), rtnetlink::Error>
265where
266 F: FnOnce(LinkMessageBuilder<LinkUnspec>) -> LinkMessageBuilder<LinkUnspec>,
267{
268 let builder = LinkMessageBuilder::<LinkUnspec>::new().index(if_id);
269 let message = op(builder).build();
270 handle.set(message).execute().await
271}
272
273fn map_link_result(result: Result<(), rtnetlink::Error>, op: &str, if_id: u32) -> RtnlLinkResponse {
274 match result {
275 Ok(()) => RtnlLinkResponse::Success,
276 Err(rtnetlink::Error::NetlinkError(err_msg)) => {
277 let io_err = err_msg.to_io();
278 if io_err.kind() == ErrorKind::NotFound {
279 RtnlLinkResponse::NotFound
280 } else {
281 log::warn!("Failed to {} for ifindex {}: {}", op, if_id, io_err);
282 RtnlLinkResponse::Failed
283 }
284 }
285 Err(err) => {
286 log::warn!("Failed to {} for ifindex {}: {}", op, if_id, err);
287 RtnlLinkResponse::Failed
288 }
289 }
290}
291
292pub(crate) async fn run_server(mut server: Server, mut handle: rtnetlink::LinkHandle) {
293 'reqloop: while let Some((req, respond)) = server.accept().await {
294 match req {
295 RtnlLinkRequest::InterfaceGet { if_id } => {
296 if if_id == 0 {
297 respond(RtnlLinkResponse::NotFound);
298 continue 'reqloop;
299 }
300
301 let response = handle.get().match_index(if_id).execute();
302 futures::pin_mut!(response);
303 while let Ok(Some(response)) = response.try_next().await {
304 let mut if_name = None;
305 for attr in response.attributes.iter() {
306 if let netlink_packet_route::link::LinkAttribute::IfName(name) = attr {
307 if_name = Some(name.clone());
308 }
309 }
310
311 if let Some(name) = if_name {
312 respond(RtnlLinkResponse::Interface(Interface {
313 if_id,
314 if_name: name,
315 }));
316 continue 'reqloop;
317 }
318 }
319 respond(RtnlLinkResponse::NotFound);
320 }
321 RtnlLinkRequest::InterfaceGetByName { if_name } => {
322 let response = handle.get().match_name(if_name.to_owned()).execute();
323 futures::pin_mut!(response);
324 while let Ok(Some(response)) = response.try_next().await {
325 let if_index = response.header.index;
326 if if_index == 0 {
327 continue;
328 }
329
330 respond(RtnlLinkResponse::Interface(Interface {
331 if_id: if_index,
332 if_name: if_name.to_owned(),
333 }));
334 continue 'reqloop;
335 }
336 respond(RtnlLinkResponse::NotFound);
337 }
338 RtnlLinkRequest::MacAddrGet { if_id } => {
339 let if_index = if_id;
340 if if_index == 0 {
341 respond(RtnlLinkResponse::NotFound);
342 continue 'reqloop;
343 }
344 let response = handle.get().match_index(if_index).execute();
345 futures::pin_mut!(response);
346 while let Ok(Some(response)) = response.try_next().await {
347 for link in response.attributes.iter() {
348 match link {
349 netlink_packet_route::link::LinkAttribute::Address(addr) => {
350 if addr.len() < 6 {
351 continue;
352 }
353 let mut mac_bytes = [0u8; 6];
354 mac_bytes.copy_from_slice(&addr[..6]);
355 respond(RtnlLinkResponse::MacAddr(MacAddr::new(mac_bytes)));
356 continue 'reqloop;
357 }
358 _ => {}
359 }
360 }
361 }
362 respond(RtnlLinkResponse::NotFound);
363 }
364 RtnlLinkRequest::MtuGet { if_id } => {
365 if if_id == 0 {
366 respond(RtnlLinkResponse::NotFound);
367 continue 'reqloop;
368 }
369
370 let response = handle.get().match_index(if_id).execute();
371 futures::pin_mut!(response);
372 while let Ok(Some(response)) = response.try_next().await {
373 for link in response.attributes.iter() {
374 if let netlink_packet_route::link::LinkAttribute::Mtu(mtu) = link {
375 respond(RtnlLinkResponse::Mtu(*mtu));
376 continue 'reqloop;
377 }
378 }
379 }
380 respond(RtnlLinkResponse::NotFound);
381 }
382 RtnlLinkRequest::InterfaceList => {
383 let mut interfaces = Vec::new();
384 let response = handle.get().execute();
385 futures::pin_mut!(response);
386 while let Ok(Some(response)) = response.try_next().await {
387 let if_index = response.header.index;
388 let mut if_name = None;
389 for link in response.attributes.iter() {
390 match link {
391 netlink_packet_route::link::LinkAttribute::IfName(name) => {
392 if_name = Some(name.clone());
393 }
394 _ => {}
395 }
396 }
397
398 if let Some(name) = if_name {
399 if if_index == 0 {
400 continue;
401 }
402
403 interfaces.push(Interface {
404 if_id: if_index,
405 if_name: name,
406 });
407 }
408 }
409 respond(RtnlLinkResponse::InterfaceList(interfaces));
410 }
411 RtnlLinkRequest::MacAddrSet { if_id, mac_addr } => {
412 if if_id == 0 {
413 respond(RtnlLinkResponse::NotFound);
414 continue 'reqloop;
415 }
416
417 let mac_bytes = mac_addr.inner.to_vec();
418 let result =
419 apply_link_set(&handle, if_id, |builder| builder.address(mac_bytes)).await;
420 respond(map_link_result(result, "set MAC address", if_id));
421 }
422 RtnlLinkRequest::InterfaceSetAdmin { if_id, up } => {
423 if if_id == 0 {
424 respond(RtnlLinkResponse::NotFound);
425 continue 'reqloop;
426 }
427
428 let op_desc = if up {
429 "set interface up"
430 } else {
431 "set interface down"
432 };
433 let result = apply_link_set(&handle, if_id, |builder| {
434 if up { builder.up() } else { builder.down() }
435 })
436 .await;
437
438 respond(map_link_result(result, op_desc, if_id));
439 }
440 RtnlLinkRequest::InterfaceSetPromisc { if_id, enable } => {
441 if if_id == 0 {
442 respond(RtnlLinkResponse::NotFound);
443 continue 'reqloop;
444 }
445
446 let op_desc = if enable {
447 "enable promiscuous mode"
448 } else {
449 "disable promiscuous mode"
450 };
451 let result =
452 apply_link_set(&handle, if_id, |builder| builder.promiscuous(enable)).await;
453
454 respond(map_link_result(result, op_desc, if_id));
455 }
456 RtnlLinkRequest::InterfaceSetArp { if_id, enable } => {
457 if if_id == 0 {
458 respond(RtnlLinkResponse::NotFound);
459 continue 'reqloop;
460 }
461
462 let op_desc = if enable { "enable ARP" } else { "disable ARP" };
463 let result = apply_link_set(&handle, if_id, |builder| builder.arp(enable)).await;
464
465 respond(map_link_result(result, op_desc, if_id));
466 }
467 RtnlLinkRequest::InterfaceSetMtu { if_id, mtu } => {
468 if if_id == 0 {
469 respond(RtnlLinkResponse::NotFound);
470 continue 'reqloop;
471 }
472
473 let result = apply_link_set(&handle, if_id, |builder| builder.mtu(mtu)).await;
474 respond(map_link_result(result, "set MTU", if_id));
475 }
476 RtnlLinkRequest::InterfaceRename { if_id, if_name } => {
477 if if_id == 0 {
478 respond(RtnlLinkResponse::NotFound);
479 continue 'reqloop;
480 }
481
482 let new_name = if_name.clone();
483 let result = apply_link_set(&handle, if_id, |builder| builder.name(new_name)).await;
484 let op_desc = format!("rename interface to {}", if_name);
485 respond(map_link_result(result, &op_desc, if_id));
486 }
487 RtnlLinkRequest::InterfaceSetAllMulticast { if_id, enable } => {
488 if if_id == 0 {
489 respond(RtnlLinkResponse::NotFound);
490 continue 'reqloop;
491 }
492
493 let op_desc = if enable {
494 "enable all-multicast mode"
495 } else {
496 "disable all-multicast mode"
497 };
498
499 let mut message = LinkMessageBuilder::<LinkUnspec>::new().index(if_id).build();
500 if enable {
501 message.header.flags |= LinkFlags::Allmulti;
502 } else {
503 message.header.flags.remove(LinkFlags::Allmulti);
504 }
505 message.header.change_mask |= LinkFlags::Allmulti;
506
507 let result = handle.set(message).execute().await;
508
509 respond(map_link_result(result, op_desc, if_id));
510 }
511 _ => respond(RtnlLinkResponse::NotImplemented),
512 }
513 }
514}