Send to the correct subnet
This commit is contained in:
@@ -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,4 +1,5 @@
|
||||
pub mod error;
|
||||
pub(crate) mod mac;
|
||||
pub(crate) mod config;
|
||||
mod ifaddrs;
|
||||
pub mod wolz;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user