Send to the correct subnet

This commit is contained in:
2026-07-10 18:54:07 +05:30
parent 3664e70c5d
commit abf1d8c175
3 changed files with 186 additions and 8 deletions
+163
View File
@@ -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<NetworkInterface> {
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]]
}
}
+1
View File
@@ -1,4 +1,5 @@
pub mod error;
pub(crate) mod mac;
pub(crate) mod config;
mod ifaddrs;
pub mod wolz;
+18 -4
View File
@@ -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")
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)
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")
.send_to(&magic_bytes, &broadcast_address)
.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);
println!("Magic packet sent to {} for Mac {:}", broadcast_address, mac);
}
}
Ok(())
}
}