diff --git a/README.md b/README.md index 303c613..45833eb 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/crates/wolz-lib/src/config.rs b/crates/wolz-lib/src/config.rs index f716af1..d32a1de 100644 --- a/crates/wolz-lib/src/config.rs +++ b/crates/wolz-lib/src/config.rs @@ -47,7 +47,7 @@ impl Config { &nick.name ))) .map(|mac| mac.clone()), - Device::IpV4(ip_v4) => todo!(), + Device::IpV4(_) => todo!(), } } diff --git a/crates/wolz-lib/src/ifaddrs.rs b/crates/wolz-lib/src/ifaddrs.rs index 048137e..f9b6fb5 100644 --- a/crates/wolz-lib/src/ifaddrs.rs +++ b/crates/wolz-lib/src/ifaddrs.rs @@ -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 { @@ -93,11 +105,7 @@ pub fn get_network_interfaces() -> Vec { 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 { // 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); }; } diff --git a/crates/wolz-lib/src/wolz.rs b/crates/wolz-lib/src/wolz.rs index 3fdc79f..9f72fbd 100644 --- a/crates/wolz-lib/src/wolz.rs +++ b/crates/wolz-lib/src/wolz.rs @@ -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(()) +}