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
- 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
@@ -9,31 +10,27 @@ Run `wolz` to view the help text.
## 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
$> # Update `enp42s0` to your interface name.
$> # To find out the interface to which the kernel
$> # 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
$> sudo tcpdump -i enp42s0 udp and port 9 -n -XX
$>
$> # Sample output for mac 04:7c:16:6e:c0:2f
0x0000: 4500 0082 6153 4000 4011 ceb6 0a60 0002 E...aS@.@....`..
0x0010: ffff ffff 8bd0 0009 006e b739 ffff ffff .........n.9....
0x0020: ffff 047c 166e c02f 047c 166e c02f 047c ...|.n./.|.n./.|
0x0030: 166e c02f 047c 166e c02f 047c 166e c02f .n./.|.n./.|.n./
0x0040: 047c 166e c02f 047c 166e c02f 047c 166e .|.n./.|.n./.|.n
0x0050: c02f 047c 166e c02f 047c 166e c02f 047c ./.|.n./.|.n./.|
0x0060: 166e c02f 047c 166e c02f 047c 166e c02f .n./.|.n./.|.n./
0x0070: 047c 166e c02f 047c 166e c02f 047c 166e .|.n./.|.n./.|.n
0x0080: c02f ./
$> # Sample output for mac aa:bb:cc:dd:ee:ff
18:56:47.573848 IP 192.168.0.10.34801 > 192.168.0.255.9: UDP, length 102
0x0000: ffff ffff ffff dca6 324f e820 0800 4500 ........2O....E.
0x0010: 0082 adf8 4000 4011 0a19 c0a8 000a c0a8 ....@.@.........
0x0020: 00ff 87f1 0009 006e 8b27 ffff ffff ffff .......n.'......
0x0030: aabb ccdd eeff aabb ccdd eeff aabb ccdd ................
0x0040: eeff aabb ccdd eeff aabb ccdd eeff aabb ................
0x0050: ccdd eeff aabb ccdd eeff aabb ccdd eeff ................
0x0060: aabb ccdd eeff aabb ccdd eeff aabb ccdd ................
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
```
+1 -1
View File
@@ -47,7 +47,7 @@ impl Config {
&nick.name
)))
.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 name: String,
pub ip: Ipv4Addr,
pub netmask: Ipv4Addr,
pub _netmask: 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> {
@@ -93,11 +105,7 @@ pub fn get_network_interfaces() -> Vec<NetworkInterface> {
let netmask = read_sa_ip(sa_netmask);
let broadcast = calculate_broadcast_address(ip, netmask);
let network_address = NetworkAddress {
ip,
netmask,
broadcast,
};
let network_address = NetworkAddress { ip, netmask, broadcast };
network_addresses.insert(name, network_address);
} 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
for (n, m) in link_addresses {
if let Some(n_addr) = network_addresses.get(&n) {
let network_interface = NetworkInterface {
name: n.to_string(),
ip: n_addr.ip,
netmask: n_addr.netmask,
broadcast: n_addr.broadcast,
mac: m.mac,
};
let network_interface = NetworkInterface::create(n, n_addr.ip, n_addr.netmask, n_addr.broadcast, m.mac);
network_interfaces.push(network_interface);
};
}
+37 -13
View File
@@ -3,7 +3,7 @@ use std::net::UdpSocket;
use crate::{
config::Config,
error::{WError, WResult},
ifaddrs,
ifaddrs::{self},
};
pub struct Wolz {
@@ -22,26 +22,50 @@ impl Wolz {
mac.copy_magic_bytes(&mut magic_bytes);
let interfaces = ifaddrs::get_network_interfaces();
let mut sent_addresses = Vec::new();
for i in &interfaces {
if sent_addresses.contains(&i.broadcast) {
continue;
}
// man systemd.net-naming-scheme
// Only broadcast to local network
let iname = &i.name;
if iname.starts_with("en") || iname.starts_with("wl") || iname.starts_with("eth") {
let address = format!("{}:0", i.ip);
let broadcast_address = format!("{}:9", i.broadcast);
let socket = UdpSocket::bind(&address)
.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_address)
.map_err(|e| WError::UdpSocketBindError(format!("couldn't send data because {}", e)))?;
println!("Magic packet sent to {} for Mac {:}", broadcast_address, mac);
let send_addr = format!("{}:0", i.ip);
let broadcast_addr = format!("{}:9", i.broadcast);
match try_send_magic_bytes(&magic_bytes, &send_addr, &broadcast_addr) {
Ok(()) => {
println!("Sent magic pkt {} -> {} for Mac {}", send_addr, broadcast_addr, mac);
sent_addresses.push(i.broadcast);
}
Err(e) => {
println!(
"Unable to send magic pkt {} -> {} for Mac {}, because {}",
send_addr, broadcast_addr, mac, e
);
}
}
}
}
if sent_addresses.len() == 0 {
println!("Did not send to any address, are you connected to local network?");
}
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(())
}