Do not send duplicates
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user