From abf1d8c1750dfe96825b47eb17930c8efa3cb3a4 Mon Sep 17 00:00:00 2001 From: cool-mist Date: Fri, 10 Jul 2026 18:54:07 +0530 Subject: [PATCH] Send to the correct subnet --- crates/wolz-lib/src/ifaddrs.rs | 163 +++++++++++++++++++++++++++++++++ crates/wolz-lib/src/lib.rs | 1 + crates/wolz-lib/src/wolz.rs | 30 ++++-- 3 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 crates/wolz-lib/src/ifaddrs.rs diff --git a/crates/wolz-lib/src/ifaddrs.rs b/crates/wolz-lib/src/ifaddrs.rs new file mode 100644 index 0000000..048137e --- /dev/null +++ b/crates/wolz-lib/src/ifaddrs.rs @@ -0,0 +1,163 @@ +use std::collections::HashMap; +use std::ffi::{CStr, c_uchar}; +use std::net::Ipv4Addr; +use std::os::raw::{c_char, c_int, c_uint, c_ushort, c_void}; + +#[repr(C)] +struct IfAddrs { + ifa_next: *mut IfAddrs, + ifa_name: *mut c_char, + ifa_flags: c_uint, + ifa_addr: *mut SockAddr, + ifa_netmask: *mut SockAddr, + ifa_ifu: *mut SockAddr, + ifa_data: *mut c_void, +} + +#[repr(C)] +struct SockAddr { + sa_family: c_ushort, + sa_data: [c_char; 14], +} + +#[repr(C)] +struct SockAddrIn { + sin_family: c_ushort, + sin_port: c_ushort, + sin_addr: u32, // network byte order + sin_zero: [u8; 8], +} + +#[repr(C)] +struct SockAddrLl { + sll_family: c_ushort, + sll_protocol: c_ushort, + sll_ifindex: c_int, + sll_hatype: c_ushort, + sll_pkttype: c_uchar, + sll_halen: c_uchar, + sll_addr: [c_uchar; 8], +} + +const AF_INET: c_ushort = 2; +const AF_PACKET: c_ushort = 17; + +unsafe extern "C" { + fn getifaddrs(ifap: *mut *mut IfAddrs) -> c_int; + fn freeifaddrs(ifa: *mut IfAddrs); +} + +struct NetworkAddress { + ip: Ipv4Addr, + netmask: Ipv4Addr, + broadcast: Ipv4Addr, +} + +struct LinkAddress { + mac: [u8; 6], +} + +pub struct NetworkInterface { + pub name: String, + pub ip: Ipv4Addr, + pub netmask: Ipv4Addr, + pub broadcast: Ipv4Addr, + pub mac: [u8; 6], +} + +pub fn get_network_interfaces() -> Vec { + let mut network_addresses = HashMap::new(); + let mut link_addresses = HashMap::new(); + let mut addrs: *mut IfAddrs = std::ptr::null_mut(); + + unsafe { + if getifaddrs(&mut addrs) != 0 { + eprintln!("getifaddrs failed"); + return Vec::new(); + } + + let mut cur = addrs; + while !cur.is_null() { + let ifa = &*cur; + let name = CStr::from_ptr(ifa.ifa_name).to_string_lossy().to_string(); + if ifa.ifa_addr.is_null() { + cur = ifa.ifa_next; + continue; + } + + let family = (*ifa.ifa_addr).sa_family; + if family == AF_INET { + let sa_ip = ifa.ifa_addr as *const SockAddrIn; + let sa_netmask = ifa.ifa_netmask as *const SockAddrIn; + let ip = read_sa_ip(sa_ip); + let netmask = read_sa_ip(sa_netmask); + let broadcast = calculate_broadcast_address(ip, netmask); + + let network_address = NetworkAddress { + ip, + netmask, + broadcast, + }; + + network_addresses.insert(name, network_address); + } else if family == AF_PACKET { + let sock_addr_ll = ifa.ifa_addr as *const SockAddrLl; + let mac = read_sa_ll(sock_addr_ll); + + let link_address = LinkAddress { mac }; + + link_addresses.insert(name, link_address); + } + + cur = ifa.ifa_next; + } + + freeifaddrs(addrs); + } + + let mut network_interfaces = Vec::new(); + + // 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, + }; + + network_interfaces.push(network_interface); + }; + } + + network_interfaces +} + +fn calculate_broadcast_address(ip: Ipv4Addr, netmask: Ipv4Addr) -> Ipv4Addr { + let ip_octets = ip.octets(); + let netmask_octets = netmask.octets(); + + let mut broadcast_octets = [0u8; 4]; + let mut broadcast_octet_index = 0; + for (i, m) in ip_octets.iter().zip(netmask_octets) { + broadcast_octets[broadcast_octet_index] = i | 255 - m; + broadcast_octet_index += 1; + } + + Ipv4Addr::from_octets(broadcast_octets) +} + +fn read_sa_ip(ifa_addr: *const SockAddrIn) -> Ipv4Addr { + unsafe { + return Ipv4Addr::from(u32::from_be((*ifa_addr).sin_addr)); + } +} + +fn read_sa_ll(ifa_addr: *const SockAddrLl) -> [u8; 6] { + unsafe { + let mac = (*ifa_addr).sll_addr; + [mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]] + } +} diff --git a/crates/wolz-lib/src/lib.rs b/crates/wolz-lib/src/lib.rs index 8b70e83..096c2ed 100644 --- a/crates/wolz-lib/src/lib.rs +++ b/crates/wolz-lib/src/lib.rs @@ -1,4 +1,5 @@ pub mod error; pub(crate) mod mac; pub(crate) mod config; +mod ifaddrs; pub mod wolz; diff --git a/crates/wolz-lib/src/wolz.rs b/crates/wolz-lib/src/wolz.rs index f20c7cc..3fdc79f 100644 --- a/crates/wolz-lib/src/wolz.rs +++ b/crates/wolz-lib/src/wolz.rs @@ -3,6 +3,7 @@ use std::net::UdpSocket; use crate::{ config::Config, error::{WError, WResult}, + ifaddrs, }; pub struct Wolz { @@ -20,14 +21,27 @@ impl Wolz { let mut magic_bytes = [0u8; 102]; mac.copy_magic_bytes(&mut magic_bytes); - let socket = UdpSocket::bind("0.0.0.0:0") - .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, "255.255.255.255:9") - .map_err(|e| WError::UdpSocketBindError(format!("couldn't send data because {}", e)))?; - println!("Magic packet sent to 255.255.255.255:9 for Mac {:}", mac); + let interfaces = ifaddrs::get_network_interfaces(); + for i in &interfaces { + // 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); + } + } + Ok(()) } }