Do not send duplicates

This commit is contained in:
2026-07-10 19:17:57 +05:30
parent abf1d8c175
commit cc5e33c31a
4 changed files with 70 additions and 48 deletions
+16 -19
View File
@@ -1,7 +1,8 @@
# Wolz # Wolz
- Broadcast Wake on Lan (WoL) magic packet to network devices in the local network. - Broadcast Wake on Lan (WoL) magic packet to network devices in the local network.
- WoL only works if the target network interface supports it and is not in low power mode. Check [arch wiki](https://wiki.archlinux.org/title/Wake-on-LAN) on this subject to setup your device to wakup - WoL only works if the target network interface supports it and is not in low power mode.
- Check [arch wiki](https://wiki.archlinux.org/title/Wake-on-LAN) on this subject to setup your device to Wakup on LAN.
## Usage ## Usage
@@ -9,31 +10,27 @@ Run `wolz` to view the help text.
## Troubleshooting ## Troubleshooting
This tool broadcasts the magic packet to the limited broadcast address 255.255.255.255 and port 9. This tool broadcasts the magic packet to the broadcast address (and port 9) of all networks the physical interfaces connect to.
Install `tcpdump` to inspect the network traffic published to the limited broadcast address (255.255.255.255) and port 9 Install `tcpdump` to inspect the network traffic on the receiving node. And execute the tool from the source node.
```bash ```bash
$> # Update `enp42s0` to your interface name. $> # Update `enp42s0` to your interface name.
$> # To find out the interface to which the kernel $> sudo tcpdump -i enp42s0 udp and port 9 -n -XX
$> # routes the packets to, do
$> # ip route get 255.255.255.255
$> sudo tcpdump -i enp42s0 udp and host 255.255.255.255 and port 9 -n -XX
$> $>
$> # Sample output for mac 04:7c:16:6e:c0:2f $> # Sample output for mac aa:bb:cc:dd:ee:ff
0x0000: 4500 0082 6153 4000 4011 ceb6 0a60 0002 E...aS@.@....`.. 18:56:47.573848 IP 192.168.0.10.34801 > 192.168.0.255.9: UDP, length 102
0x0010: ffff ffff 8bd0 0009 006e b739 ffff ffff .........n.9.... 0x0000: ffff ffff ffff dca6 324f e820 0800 4500 ........2O....E.
0x0020: ffff 047c 166e c02f 047c 166e c02f 047c ...|.n./.|.n./.| 0x0010: 0082 adf8 4000 4011 0a19 c0a8 000a c0a8 ....@.@.........
0x0030: 166e c02f 047c 166e c02f 047c 166e c02f .n./.|.n./.|.n./ 0x0020: 00ff 87f1 0009 006e 8b27 ffff ffff ffff .......n.'......
0x0040: 047c 166e c02f 047c 166e c02f 047c 166e .|.n./.|.n./.|.n 0x0030: aabb ccdd eeff aabb ccdd eeff aabb ccdd ................
0x0050: c02f 047c 166e c02f 047c 166e c02f 047c ./.|.n./.|.n./.| 0x0040: eeff aabb ccdd eeff aabb ccdd eeff aabb ................
0x0060: 166e c02f 047c 166e c02f 047c 166e c02f .n./.|.n./.|.n./ 0x0050: ccdd eeff aabb ccdd eeff aabb ccdd eeff ................
0x0070: 047c 166e c02f 047c 166e c02f 047c 166e .|.n./.|.n./.|.n 0x0060: aabb ccdd eeff aabb ccdd eeff aabb ccdd ................
0x0080: c02f ./ 0x0070: eeff aabb ccdd eeff aabb ccdd eeff aabb ................
0x0080: ccdd eeff aabb ccdd eeff aabb ccdd eeff ................
``` ```
If you have VPN enabled, depending on the configuration the kernel might route it to the tunnel interface, and the tool will not work.
To debug TCP packets intended to wolz-daemon, run To debug TCP packets intended to wolz-daemon, run
``` ```
+1 -1
View File
@@ -47,7 +47,7 @@ impl Config {
&nick.name &nick.name
))) )))
.map(|mac| mac.clone()), .map(|mac| mac.clone()),
Device::IpV4(ip_v4) => todo!(), Device::IpV4(_) => todo!(),
} }
} }
+16 -15
View File
@@ -60,9 +60,21 @@ struct LinkAddress {
pub struct NetworkInterface { pub struct NetworkInterface {
pub name: String, pub name: String,
pub ip: Ipv4Addr, pub ip: Ipv4Addr,
pub netmask: Ipv4Addr, pub _netmask: Ipv4Addr,
pub broadcast: Ipv4Addr, pub broadcast: Ipv4Addr,
pub mac: [u8; 6], pub _mac: [u8; 6],
}
impl NetworkInterface {
fn create(name: String, ip: Ipv4Addr, netmask: Ipv4Addr, broadcast: Ipv4Addr, mac: [u8; 6]) -> Self {
Self {
name,
ip,
_netmask: netmask,
broadcast,
_mac: mac,
}
}
} }
pub fn get_network_interfaces() -> Vec<NetworkInterface> { pub fn get_network_interfaces() -> Vec<NetworkInterface> {
@@ -93,11 +105,7 @@ pub fn get_network_interfaces() -> Vec<NetworkInterface> {
let netmask = read_sa_ip(sa_netmask); let netmask = read_sa_ip(sa_netmask);
let broadcast = calculate_broadcast_address(ip, netmask); let broadcast = calculate_broadcast_address(ip, netmask);
let network_address = NetworkAddress { let network_address = NetworkAddress { ip, netmask, broadcast };
ip,
netmask,
broadcast,
};
network_addresses.insert(name, network_address); network_addresses.insert(name, network_address);
} else if family == AF_PACKET { } else if family == AF_PACKET {
@@ -120,14 +128,7 @@ pub fn get_network_interfaces() -> Vec<NetworkInterface> {
// Assume all link_addresses have unique names for now // Assume all link_addresses have unique names for now
for (n, m) in link_addresses { for (n, m) in link_addresses {
if let Some(n_addr) = network_addresses.get(&n) { if let Some(n_addr) = network_addresses.get(&n) {
let network_interface = NetworkInterface { let network_interface = NetworkInterface::create(n, n_addr.ip, n_addr.netmask, n_addr.broadcast, m.mac);
name: n.to_string(),
ip: n_addr.ip,
netmask: n_addr.netmask,
broadcast: n_addr.broadcast,
mac: m.mac,
};
network_interfaces.push(network_interface); network_interfaces.push(network_interface);
}; };
} }
+37 -13
View File
@@ -3,7 +3,7 @@ use std::net::UdpSocket;
use crate::{ use crate::{
config::Config, config::Config,
error::{WError, WResult}, error::{WError, WResult},
ifaddrs, ifaddrs::{self},
}; };
pub struct Wolz { pub struct Wolz {
@@ -22,26 +22,50 @@ impl Wolz {
mac.copy_magic_bytes(&mut magic_bytes); mac.copy_magic_bytes(&mut magic_bytes);
let interfaces = ifaddrs::get_network_interfaces(); let interfaces = ifaddrs::get_network_interfaces();
let mut sent_addresses = Vec::new();
for i in &interfaces { for i in &interfaces {
if sent_addresses.contains(&i.broadcast) {
continue;
}
// man systemd.net-naming-scheme // man systemd.net-naming-scheme
// Only broadcast to local network // Only broadcast to local network
let iname = &i.name; let iname = &i.name;
if iname.starts_with("en") || iname.starts_with("wl") || iname.starts_with("eth") { if iname.starts_with("en") || iname.starts_with("wl") || iname.starts_with("eth") {
let address = format!("{}:0", i.ip); let send_addr = format!("{}:0", i.ip);
let broadcast_address = format!("{}:9", i.broadcast); let broadcast_addr = format!("{}:9", i.broadcast);
let socket = UdpSocket::bind(&address) match try_send_magic_bytes(&magic_bytes, &send_addr, &broadcast_addr) {
.map_err(|e| WError::UdpSocketBindError(format!("couldn't bind to socket because {}", e)))?; Ok(()) => {
socket println!("Sent magic pkt {} -> {} for Mac {}", send_addr, broadcast_addr, mac);
.set_broadcast(true) sent_addresses.push(i.broadcast);
.map_err(|e| WError::UdpSocketBindError(format!("couldn't set broadcast because {}", e)))?; }
socket Err(e) => {
.send_to(&magic_bytes, &broadcast_address) println!(
.map_err(|e| WError::UdpSocketBindError(format!("couldn't send data because {}", e)))?; "Unable to send magic pkt {} -> {} for Mac {}, because {}",
send_addr, broadcast_addr, mac, e
println!("Magic packet sent to {} for Mac {:}", broadcast_address, mac); );
}
}
} }
} }
if sent_addresses.len() == 0 {
println!("Did not send to any address, are you connected to local network?");
}
Ok(()) Ok(())
} }
} }
fn try_send_magic_bytes(magic_bytes: &[u8; 102], send_addr: &str, broadcast_addr: &str) -> WResult<()> {
let socket = UdpSocket::bind(&send_addr)
.map_err(|e| WError::UdpSocketBindError(format!("couldn't bind to socket because {}", e)))?;
socket
.set_broadcast(true)
.map_err(|e| WError::UdpSocketBindError(format!("couldn't set broadcast because {}", e)))?;
socket
.send_to(magic_bytes, &broadcast_addr)
.map_err(|e| WError::UdpSocketBindError(format!("couldn't send data because {}", e)))?;
Ok(())
}