roowho2_lib/server/rwhod/
packet_sender.rs1use nix::{ifaddrs::getifaddrs, net::if_::InterfaceFlags};
2use std::{
3 collections::HashSet,
4 net::{IpAddr, SocketAddr},
5 sync::Arc,
6};
7use tokio::{
8 net::UdpSocket,
9 time::{Duration as TokioDuration, interval},
10};
11
12use crate::{
13 proto::Whod,
14 server::{ignore_list::IgnoreList, rwhod::rwhod_status::generate_rwhod_status_update},
15};
16
17pub const RWHOD_BROADCAST_PORT: u16 = 513;
19
20#[derive(Debug, Clone)]
21pub struct RwhodSendTarget {
22 pub name: String,
24
25 pub addr: IpAddr,
29}
30
31pub fn determine_relevant_interfaces() -> anyhow::Result<Vec<RwhodSendTarget>> {
33 getifaddrs().map_err(|e| e.into()).map(|ifaces| {
34 ifaces
35 .filter(|iface| iface.flags.contains(InterfaceFlags::IFF_UP))
37 .filter(|iface| {
39 iface
40 .flags
41 .intersects(InterfaceFlags::IFF_BROADCAST | InterfaceFlags::IFF_POINTOPOINT)
42 })
43 .filter_map(|iface| {
44 let neighbor_addr = if iface.flags.contains(InterfaceFlags::IFF_BROADCAST) {
45 iface.broadcast
46 } else if iface.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
47 iface.destination
48 } else {
49 None
50 };
51
52 match neighbor_addr {
53 Some(addr) => addr
54 .as_sockaddr_in()
55 .map(|sa| IpAddr::V4(sa.ip()))
56 .or_else(|| addr.as_sockaddr_in6().map(|sa| IpAddr::V6(sa.ip())))
57 .map(|ip_addr| RwhodSendTarget {
58 name: iface.interface_name,
59 addr: ip_addr,
60 }),
61 None => None,
62 }
63 })
64 .scan(HashSet::new(), |seen, n| {
66 if seen.insert(n.name.clone()) {
67 Some(n)
68 } else {
69 None
70 }
71 })
72 .collect::<Vec<RwhodSendTarget>>()
73 })
74}
75
76pub async fn send_rwhod_packet_to_interface(
77 socket: Arc<UdpSocket>,
78 interface: &RwhodSendTarget,
79 packet: &Whod,
80) -> anyhow::Result<()> {
81 let serialized_packet = packet.to_bytes();
82
83 let target_addr = match interface.addr {
85 IpAddr::V4(addr) => SocketAddr::new(IpAddr::V4(addr), RWHOD_BROADCAST_PORT),
86 IpAddr::V6(addr) => SocketAddr::new(IpAddr::V6(addr), RWHOD_BROADCAST_PORT),
87 };
88
89 tracing::debug!(
90 "Sending rwhod packet to interface {} at address {}",
91 interface.name,
92 target_addr
93 );
94
95 socket
96 .send_to(&serialized_packet, &target_addr)
97 .await
98 .map_err(|e| anyhow::anyhow!("Failed to send rwhod packet: {}", e))?;
99
100 Ok(())
101}
102
103pub async fn rwhod_packet_sender_task(
104 socket: Arc<UdpSocket>,
105 interfaces: Vec<RwhodSendTarget>,
106 ignore_list: Option<IgnoreList>,
107) -> anyhow::Result<()> {
108 let mut interval = interval(TokioDuration::from_secs(60));
109
110 loop {
111 interval.tick().await;
112
113 let status_update = generate_rwhod_status_update(ignore_list.as_ref())?;
114
115 tracing::debug!("Generated rwhod packet: {:?}", status_update);
116
117 let packet = status_update
118 .try_into()
119 .map_err(|e| anyhow::anyhow!("{}", e))?;
120
121 for interface in &interfaces {
122 if let Err(e) = send_rwhod_packet_to_interface(socket.clone(), interface, &packet).await
123 {
124 tracing::error!(
125 "Failed to send rwhod packet on interface {}: {}",
126 interface.name,
127 e
128 );
129 }
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_determine_relevant_interfaces() {
140 let interfaces = determine_relevant_interfaces().unwrap();
141 for interface in interfaces {
142 println!("Interface: {} Address: {}", interface.name, interface.addr);
143 }
144 }
145}