dhcpclient now listens to netlink for link addition and removal
This commit is contained in:
parent
f04132bfe1
commit
65b32a4be5
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -31,9 +31,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dhcprs"
|
name = "dhcprs"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb5849fa01b515c56a6247af5121fa38bb22988e2c0f7216d93e5623417a57ea"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eui48",
|
"eui48",
|
||||||
]
|
]
|
||||||
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dhcprs = "0.1.2"
|
dhcprs = {version = "0.1.3", path = "../rust-dhcprs"}
|
||||||
nix = "0.25.0"
|
nix = "0.25.0"
|
||||||
libc = "0.2.133"
|
libc = "0.2.133"
|
||||||
eui48 = "1.1.0"
|
eui48 = "1.1.0"
|
||||||
|
271
src/main.rs
271
src/main.rs
@ -1,12 +1,22 @@
|
|||||||
use nix::sys::socket::*;
|
pub mod rtnetlink;
|
||||||
use std::net::Ipv4Addr;
|
|
||||||
use eui48::MacAddress;
|
use eui48::MacAddress;
|
||||||
|
use nix::sys::socket::*;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
use dhcprs::dhcp::DHCPOption;
|
|
||||||
use dhcprs::dhcp::DHCPMessageType;
|
use dhcprs::dhcp::DHCPMessageType;
|
||||||
|
use dhcprs::dhcp::DHCPOption;
|
||||||
|
|
||||||
fn create_dhcp_packet(xid: u32, mac: MacAddress, source_ip: Option<Ipv4Addr>, dest_ip: Option<Ipv4Addr>, options: Vec<dhcprs::dhcp::DHCPOption>) -> dhcprs::udpbuilder::RawUDPPacket {
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn create_dhcp_packet(
|
||||||
|
xid: u32,
|
||||||
|
mac: MacAddress,
|
||||||
|
source_ip: Option<Ipv4Addr>,
|
||||||
|
dest_ip: Option<Ipv4Addr>,
|
||||||
|
options: Vec<dhcprs::dhcp::DHCPOption>,
|
||||||
|
) -> dhcprs::udpbuilder::RawUDPPacket {
|
||||||
let options_bytes = dhcprs::dhcp::DHCPOption::to_bytes(options);
|
let options_bytes = dhcprs::dhcp::DHCPOption::to_bytes(options);
|
||||||
let mut vend = [0; 312];
|
let mut vend = [0; 312];
|
||||||
vend[..options_bytes.len()].copy_from_slice(&options_bytes);
|
vend[..options_bytes.len()].copy_from_slice(&options_bytes);
|
||||||
@ -24,15 +34,18 @@ fn create_dhcp_packet(xid: u32, mac: MacAddress, source_ip: Option<Ipv4Addr>, de
|
|||||||
mac,
|
mac,
|
||||||
[0; 64],
|
[0; 64],
|
||||||
[0; 128],
|
[0; 128],
|
||||||
vend
|
vend,
|
||||||
);
|
);
|
||||||
|
|
||||||
let udppacket = dhcprs::udpbuilder::UDPPacket::new(
|
let udppacket = dhcprs::udpbuilder::UDPPacket::new(
|
||||||
source_ip.unwrap_or(Ipv4Addr::new(0,0,0,0)),
|
source_ip.unwrap_or(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
dest_ip.unwrap_or(Ipv4Addr::new(255,255,255,255)),
|
dest_ip.unwrap_or(Ipv4Addr::new(255, 255, 255, 255)),
|
||||||
68,
|
68,
|
||||||
67,
|
67,
|
||||||
dhcprs::bootp::RawBOOTPPacket::from(bootppacket).as_bytes().try_into().unwrap()
|
dhcprs::bootp::RawBOOTPPacket::from(bootppacket)
|
||||||
|
.as_bytes()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return dhcprs::udpbuilder::RawUDPPacket::from(udppacket);
|
return dhcprs::udpbuilder::RawUDPPacket::from(udppacket);
|
||||||
@ -43,33 +56,46 @@ enum DHCPTransactionState {
|
|||||||
WaitingAfterDiscover,
|
WaitingAfterDiscover,
|
||||||
Request,
|
Request,
|
||||||
WaitAfterRequest,
|
WaitAfterRequest,
|
||||||
|
Renew,
|
||||||
|
WaitAfterRenew,
|
||||||
|
Rebind,
|
||||||
|
WaitafterRebind,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dhcp_client(name: String, mac: LinkAddr) {
|
fn dhcp_client(name: String, index: i32, mac: [u8; 6]) {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
// Before we can do anything else, we need to construct an LinkAddr for the mac ff:ff:ff:ff:ff:ff
|
// Before we can do anything else, we need to construct an LinkAddr for the mac ff:ff:ff:ff:ff:ff
|
||||||
|
|
||||||
println!("Starting DHCP on interface {}", name);
|
|
||||||
|
|
||||||
let sockaddr_ll = libc::sockaddr_ll {
|
let sockaddr_ll = libc::sockaddr_ll {
|
||||||
sll_family: libc::AF_PACKET as u16,
|
sll_family: libc::AF_PACKET as u16,
|
||||||
sll_halen: mac.halen() as u8,
|
sll_halen: 6,
|
||||||
sll_hatype: mac.hatype(),
|
sll_hatype: 1,
|
||||||
sll_ifindex: mac.ifindex() as i32,
|
sll_ifindex: index,
|
||||||
sll_addr: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0],
|
sll_addr: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0],
|
||||||
sll_pkttype: mac.pkttype(),
|
sll_pkttype: 0,
|
||||||
sll_protocol: 0x0008
|
sll_protocol: 0x0008,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut linkaddr = unsafe {LinkAddr::from_raw(&sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr, Some(20)).expect("Failed to create linkaddr!")};
|
let mut linkaddr = unsafe {
|
||||||
|
LinkAddr::from_raw(
|
||||||
|
&sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr,
|
||||||
|
Some(20),
|
||||||
|
)
|
||||||
|
.expect("Failed to create linkaddr!")
|
||||||
|
};
|
||||||
|
|
||||||
// Create and bind the socket
|
// Create and bind the socket
|
||||||
let socket = socket(AddressFamily::Packet, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp).expect("Failed to create socket! Permission issue?");
|
let socket = socket(
|
||||||
|
AddressFamily::Packet,
|
||||||
|
SockType::Datagram,
|
||||||
|
SockFlag::empty(),
|
||||||
|
SockProtocol::Udp,
|
||||||
|
)
|
||||||
|
.expect("Failed to create socket! Permission issue?");
|
||||||
assert!(setsockopt(socket, sockopt::Broadcast, &true).is_ok());
|
assert!(setsockopt(socket, sockopt::Broadcast, &true).is_ok());
|
||||||
assert!(bind(socket, &linkaddr).is_ok());
|
assert!(bind(socket, &linkaddr).is_ok());
|
||||||
|
|
||||||
let client_mac = MacAddress::from_bytes(&mac.addr().unwrap()).unwrap();
|
let client_mac = MacAddress::from_bytes(&mac).unwrap();
|
||||||
|
|
||||||
let mut client_addr: Option<Ipv4Addr> = None;
|
let mut client_addr: Option<Ipv4Addr> = None;
|
||||||
let mut server_addr: Option<Ipv4Addr> = None;
|
let mut server_addr: Option<Ipv4Addr> = None;
|
||||||
@ -84,10 +110,30 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
match &dhcp_state {
|
match &dhcp_state {
|
||||||
DHCPTransactionState::Discover => {
|
DHCPTransactionState::Discover => {
|
||||||
println!("Sent DHCPDiscover on {}", name);
|
println!("Sent DHCPDiscover on {}", name);
|
||||||
let _ = sendto(socket, create_dhcp_packet(xid, client_mac, None, None, vec![
|
loop {
|
||||||
DHCPOption::DHCPMessageType(DHCPMessageType::DHCPDiscover),
|
match sendto(
|
||||||
DHCPOption::End
|
socket,
|
||||||
]).as_bytes(), &linkaddr, MsgFlags::empty()).unwrap();
|
create_dhcp_packet(
|
||||||
|
xid,
|
||||||
|
client_mac,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
vec![
|
||||||
|
DHCPOption::DHCPMessageType(DHCPMessageType::DHCPDiscover),
|
||||||
|
DHCPOption::End,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
&linkaddr,
|
||||||
|
MsgFlags::empty(),
|
||||||
|
) {
|
||||||
|
Ok(_) => break,
|
||||||
|
Err(nix::errno::Errno::ENETDOWN) => {
|
||||||
|
unsafe { libc::sleep(3) };
|
||||||
|
}
|
||||||
|
Err(_) => panic!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
dhcp_state = DHCPTransactionState::WaitingAfterDiscover;
|
dhcp_state = DHCPTransactionState::WaitingAfterDiscover;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,15 +143,17 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
|
|
||||||
println!("Received {} bytes on {}", bytes, name);
|
println!("Received {} bytes on {}", bytes, name);
|
||||||
|
|
||||||
let udppacet_raw: dhcprs::udpbuilder::RawUDPPacket = unsafe {std::ptr::read(packet.as_ptr() as *const _)};
|
let udppacket_raw: dhcprs::udpbuilder::RawUDPPacket =
|
||||||
let udppacket: dhcprs::udpbuilder::UDPPacket = udppacet_raw.into();
|
unsafe { std::ptr::read(packet.as_ptr() as *const _) };
|
||||||
|
let udppacket: dhcprs::udpbuilder::UDPPacket = udppacket_raw.into();
|
||||||
|
|
||||||
if udppacket.source_port != 67 {
|
if udppacket.source_port != 67 {
|
||||||
// Not a BOOTP reply, get the next packet.
|
// Not a BOOTP reply, get the next packet.
|
||||||
continue 'dhcp_message_loop;
|
continue 'dhcp_message_loop;
|
||||||
};
|
};
|
||||||
|
|
||||||
let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket = unsafe {std::ptr::read(udppacket.get_data().as_ptr() as *const _)};
|
let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket =
|
||||||
|
unsafe { std::ptr::read(udppacket.get_data().as_ptr() as *const _) };
|
||||||
let bootppacket: dhcprs::bootp::BOOTPPacket = bootppacket_raw.into();
|
let bootppacket: dhcprs::bootp::BOOTPPacket = bootppacket_raw.into();
|
||||||
|
|
||||||
if bootppacket.xid != xid {
|
if bootppacket.xid != xid {
|
||||||
@ -113,9 +161,20 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
continue 'dhcp_message_loop;
|
continue 'dhcp_message_loop;
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]);
|
let options =
|
||||||
|
dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]);
|
||||||
|
|
||||||
if !options.iter().find(|&x| if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPOffer) = x {true} else {false}).is_some() {
|
if !options
|
||||||
|
.iter()
|
||||||
|
.find(|&x| {
|
||||||
|
if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPOffer) = x {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
// We got a response but it wasn't the expected message, try again with another transaction.
|
// We got a response but it wasn't the expected message, try again with another transaction.
|
||||||
continue 'dhcp_transaction;
|
continue 'dhcp_transaction;
|
||||||
}
|
}
|
||||||
@ -125,8 +184,14 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
client_addr = bootppacket.yiaddr;
|
client_addr = bootppacket.yiaddr;
|
||||||
server_addr = bootppacket.siaddr;
|
server_addr = bootppacket.siaddr;
|
||||||
|
|
||||||
println!("Got client address: {}", client_addr.unwrap_or(Ipv4Addr::new(0,0,0,0)));
|
println!(
|
||||||
println!("Got server address: {}", server_addr.unwrap_or(Ipv4Addr::new(0,0,0,0)));
|
"Got client address: {}",
|
||||||
|
client_addr.unwrap_or(Ipv4Addr::new(0, 0, 0, 0))
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Got server address: {}",
|
||||||
|
server_addr.unwrap_or(Ipv4Addr::new(0, 0, 0, 0))
|
||||||
|
);
|
||||||
|
|
||||||
// Update the linkaddr to be the server's actual hardware address rather than the broadcast MAC.
|
// Update the linkaddr to be the server's actual hardware address rather than the broadcast MAC.
|
||||||
let mut mac: [u8; 8] = [0; 8];
|
let mut mac: [u8; 8] = [0; 8];
|
||||||
@ -138,21 +203,41 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
sll_ifindex: sockaddr_ll.sll_ifindex,
|
sll_ifindex: sockaddr_ll.sll_ifindex,
|
||||||
sll_addr: mac,
|
sll_addr: mac,
|
||||||
sll_pkttype: sockaddr_ll.sll_pkttype,
|
sll_pkttype: sockaddr_ll.sll_pkttype,
|
||||||
sll_protocol: sockaddr_ll.sll_protocol
|
sll_protocol: sockaddr_ll.sll_protocol,
|
||||||
};
|
};
|
||||||
|
|
||||||
linkaddr = unsafe {LinkAddr::from_raw(&new_sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr, Some(20)).expect("Failed to update linkaddr to server's hardware address!")};
|
linkaddr = unsafe {
|
||||||
|
LinkAddr::from_raw(
|
||||||
|
&new_sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr,
|
||||||
|
Some(20),
|
||||||
|
)
|
||||||
|
.expect("Failed to update linkaddr to server's hardware address!")
|
||||||
|
};
|
||||||
|
|
||||||
dhcp_state = DHCPTransactionState::Request;
|
dhcp_state = DHCPTransactionState::Request;
|
||||||
}
|
}
|
||||||
|
|
||||||
DHCPTransactionState::Request => {
|
DHCPTransactionState::Request => {
|
||||||
println!("Sent DHCPRequest on {}", name);
|
println!("Sent DHCPRequest on {}", name);
|
||||||
let _ = sendto(socket, create_dhcp_packet(xid, client_mac, client_addr, server_addr, vec![
|
let _ = sendto(
|
||||||
DHCPOption::DHCPMessageType(DHCPMessageType::DHCPRequest),
|
socket,
|
||||||
DHCPOption::ParameterRequest(vec![1, 3, 6, 28]),
|
create_dhcp_packet(
|
||||||
DHCPOption::End
|
xid,
|
||||||
]).as_bytes(), &linkaddr, MsgFlags::empty());
|
client_mac,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
vec![
|
||||||
|
DHCPOption::DHCPMessageType(DHCPMessageType::DHCPRequest),
|
||||||
|
DHCPOption::RequestIPAddress(client_addr.unwrap()),
|
||||||
|
DHCPOption::ServerIdentifier(server_addr.unwrap()),
|
||||||
|
DHCPOption::ParameterRequest(vec![1, 3, 6, 28, 121]),
|
||||||
|
DHCPOption::End,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
&linkaddr,
|
||||||
|
MsgFlags::empty(),
|
||||||
|
);
|
||||||
dhcp_state = DHCPTransactionState::WaitAfterRequest;
|
dhcp_state = DHCPTransactionState::WaitAfterRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,15 +246,17 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
let (bytes, _) = recvfrom::<LinkAddr>(socket, &mut packet).unwrap();
|
let (bytes, _) = recvfrom::<LinkAddr>(socket, &mut packet).unwrap();
|
||||||
println!("Received {} bytes on {}", bytes, name);
|
println!("Received {} bytes on {}", bytes, name);
|
||||||
|
|
||||||
let udppacet_raw: dhcprs::udpbuilder::RawUDPPacket = unsafe {std::ptr::read(packet.as_ptr() as *const _)};
|
let udppacket_raw: dhcprs::udpbuilder::RawUDPPacket =
|
||||||
let udppacket: dhcprs::udpbuilder::UDPPacket = udppacet_raw.into();
|
unsafe { std::ptr::read(packet.as_ptr() as *const _) };
|
||||||
|
let udppacket: dhcprs::udpbuilder::UDPPacket = udppacket_raw.into();
|
||||||
|
|
||||||
if udppacket.source_port != 67 {
|
if udppacket.source_port != 67 {
|
||||||
// Not a BOOTP reply, get the next packet.
|
// Not a BOOTP reply, get the next packet.
|
||||||
continue 'dhcp_message_loop;
|
continue 'dhcp_message_loop;
|
||||||
};
|
};
|
||||||
|
|
||||||
let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket = unsafe {std::ptr::read(udppacket.get_data().as_ptr() as *const _)};
|
let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket =
|
||||||
|
unsafe { std::ptr::read(udppacket.get_data().as_ptr() as *const _) };
|
||||||
let bootppacket: dhcprs::bootp::BOOTPPacket = bootppacket_raw.into();
|
let bootppacket: dhcprs::bootp::BOOTPPacket = bootppacket_raw.into();
|
||||||
|
|
||||||
if bootppacket.xid != xid {
|
if bootppacket.xid != xid {
|
||||||
@ -177,9 +264,20 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
continue 'dhcp_message_loop;
|
continue 'dhcp_message_loop;
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]);
|
let options =
|
||||||
|
dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]);
|
||||||
|
|
||||||
if !options.iter().find(|&x| if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPACK) = x {true} else {false}).is_some() {
|
if !options
|
||||||
|
.iter()
|
||||||
|
.find(|&x| {
|
||||||
|
if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPACK) = x {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
// Wasn't an ACK, probably NAK, try again.
|
// Wasn't an ACK, probably NAK, try again.
|
||||||
println!("Did not receive ACK from DHCPRequest, sleeping for 10secs before continuing.");
|
println!("Did not receive ACK from DHCPRequest, sleeping for 10secs before continuing.");
|
||||||
std::thread::sleep(core::time::Duration::new(10, 0));
|
std::thread::sleep(core::time::Duration::new(10, 0));
|
||||||
@ -192,11 +290,9 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
println!("Received: {:?}", option);
|
println!("Received: {:?}", option);
|
||||||
|
|
||||||
match option {
|
match option {
|
||||||
DHCPOption::IPAddressLeaseTime(n) => {
|
DHCPOption::IPAddressLeaseTime(n) => sleep_time = n,
|
||||||
sleep_time = n
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,22 +300,75 @@ fn dhcp_client(name: String, mac: LinkAddr) {
|
|||||||
std::thread::sleep(core::time::Duration::new(sleep_time.into(), 0));
|
std::thread::sleep(core::time::Duration::new(sleep_time.into(), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => ()
|
_ => panic!("Fail!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zascii(slice: &[libc::c_char]) -> String {
|
||||||
|
String::from_iter(
|
||||||
|
slice
|
||||||
|
.iter()
|
||||||
|
.take_while(|c| **c != 0)
|
||||||
|
.map(|c| *c as u8 as char),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let threads: Vec<std::thread::JoinHandle<()>> = nix::ifaddrs::getifaddrs().unwrap()
|
let mut thread_map: HashMap<[u8; 6], libc::c_int> = HashMap::new();
|
||||||
.filter(|x| x.address.is_some())
|
let iterator = rtnetlink::new_interface_iterator().unwrap();
|
||||||
.filter(|x| x.address.unwrap().as_link_addr().is_some())
|
|
||||||
.filter(|x| x.address.unwrap().as_link_addr().unwrap().hatype() == 1) // Only perform DHCP on Ethernet interfaces.
|
for message in iterator {
|
||||||
.map(|x| (x.interface_name, *x.address.unwrap().as_link_addr().unwrap()))
|
let interface: rtnetlink::InterfaceDetails;
|
||||||
.map(|(name, mac)| std::thread::spawn(move || { dhcp_client(name, mac)} ))
|
|
||||||
.collect();
|
match message {
|
||||||
|
rtnetlink::MessageType::NewLink(i) => {
|
||||||
for thread in threads {
|
interface = i;
|
||||||
let _ = thread.join();
|
let name = zascii(&interface.name);
|
||||||
|
println!("New Link");
|
||||||
|
|
||||||
|
if let None = thread_map.get(&interface.hwaddr) {
|
||||||
|
println!("Starting DHCP on {}", name);
|
||||||
|
unsafe {
|
||||||
|
match libc::fork() {
|
||||||
|
x if x < 0 => panic!("Failed to fork!"),
|
||||||
|
0 => dhcp_client(name, interface.index, interface.hwaddr),
|
||||||
|
pid => {
|
||||||
|
println!("Started DHCP on {} PID {}", name, pid);
|
||||||
|
thread_map.insert(interface.hwaddr, pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rtnetlink::MessageType::DelLink(i) => {
|
||||||
|
interface = i;
|
||||||
|
let name = zascii(&interface.name);
|
||||||
|
println!("Del Link");
|
||||||
|
|
||||||
|
if let Some(pid) = thread_map.get(&interface.hwaddr) {
|
||||||
|
println!("Stopping DHCP on {}", name);
|
||||||
|
unsafe {
|
||||||
|
libc::kill(*pid, libc::SIGTERM);
|
||||||
|
}
|
||||||
|
thread_map.remove(&interface.hwaddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = zascii(&interface.name);
|
||||||
|
|
||||||
|
println!("Name: {}", name);
|
||||||
|
println!(
|
||||||
|
"MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
|
||||||
|
interface.hwaddr[0],
|
||||||
|
interface.hwaddr[1],
|
||||||
|
interface.hwaddr[2],
|
||||||
|
interface.hwaddr[3],
|
||||||
|
interface.hwaddr[4],
|
||||||
|
interface.hwaddr[5]
|
||||||
|
);
|
||||||
|
println!("Index: {}", interface.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
292
src/rtnetlink.rs
Normal file
292
src/rtnetlink.rs
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
use eui48::MacAddress;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct ifinfomsg {
|
||||||
|
ifi_family: libc::__u8,
|
||||||
|
ifi_pad: libc::__u8,
|
||||||
|
ifi_type: libc::__u16,
|
||||||
|
ifi_index: libc::c_int,
|
||||||
|
ifi_flags: libc::__u32,
|
||||||
|
ifi_change: libc::__u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const nlmsg_alignto: u32 = 4;
|
||||||
|
|
||||||
|
macro_rules! nlmsg_align {
|
||||||
|
($len:expr) => {
|
||||||
|
($len as u32 + (nlmsg_alignto - 1)) & !(nlmsg_alignto - 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nlmsg_hdrlen: usize = nlmsg_align!(std::mem::size_of::<libc::nlmsghdr>()) as usize;
|
||||||
|
|
||||||
|
macro_rules! nlmsg_length {
|
||||||
|
($len:expr) => {
|
||||||
|
$len as u32 + nlmsg_hdrlen as u32
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! nlmsg_space {
|
||||||
|
($len:expr) => {
|
||||||
|
nlmsg_align!(nlmsg_length!($len))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn nlmsg_data(nlh: *const libc::nlmsghdr) -> *const u8 {
|
||||||
|
return (nlh as *const u8).offset(nlmsg_hdrlen as isize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn nlmsg_next(nlh: *const libc::nlmsghdr, len: &mut u32) -> *const libc::nlmsghdr {
|
||||||
|
*len -= nlmsg_align!((*nlh).nlmsg_len);
|
||||||
|
return (nlh as *const u8).offset(nlmsg_align!((*nlh).nlmsg_len) as isize)
|
||||||
|
as *const libc::nlmsghdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn nlmsg_ok(nlh: *const libc::nlmsghdr, len: &mut u32) -> bool {
|
||||||
|
return *len >= std::mem::size_of::<libc::nlmsghdr>() as u32
|
||||||
|
&& (*nlh).nlmsg_len >= std::mem::size_of::<libc::nlmsghdr>() as u32
|
||||||
|
&& (*nlh).nlmsg_len <= *len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn nlmsg_payload(nlh: *const libc::nlmsghdr, len: &mut u32) -> u32 {
|
||||||
|
return (*nlh).nlmsg_len - nlmsg_space!(*len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct rtattr {
|
||||||
|
rta_len: libc::c_ushort,
|
||||||
|
rta_type: libc::c_ushort,
|
||||||
|
}
|
||||||
|
|
||||||
|
const rta_alignto: u32 = 4;
|
||||||
|
|
||||||
|
macro_rules! rta_align {
|
||||||
|
($len:expr) => {
|
||||||
|
($len as u32 + (rta_alignto - 1)) & !(rta_alignto - 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn rta_ok(rta: *const rtattr, len: &mut u32) -> bool {
|
||||||
|
return *len >= std::mem::size_of::<rtattr>() as u32
|
||||||
|
&& (*rta).rta_len >= std::mem::size_of::<rtattr>() as libc::c_ushort
|
||||||
|
&& (*rta).rta_len <= *len as libc::c_ushort;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn rta_next(rta: *const rtattr, len: &mut u32) -> *const rtattr {
|
||||||
|
*len -= rta_align!((*rta).rta_len);
|
||||||
|
return (rta as *const u8).offset(nlmsg_align!((*rta).rta_len) as isize) as *const rtattr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn rta_length(len: u32) -> u32 {
|
||||||
|
return std::mem::size_of::<rtattr>() as u32 + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn rta_space(len: u32) -> u32 {
|
||||||
|
rta_align!(rta_length(len))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn rta_data(rta: *const rtattr) -> *const u8 {
|
||||||
|
return (rta as *const u8).offset(rta_length(0) as isize);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RtAttrs {
|
||||||
|
current_attr: *const rtattr,
|
||||||
|
current_size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for RtAttrs {
|
||||||
|
type Item = *const rtattr;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
unsafe {
|
||||||
|
if rta_ok(self.current_attr, &mut self.current_size) {
|
||||||
|
let hdr = self.current_attr;
|
||||||
|
self.current_attr = rta_next(self.current_attr, &mut self.current_size);
|
||||||
|
return Some(hdr);
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn ifla_rta(r: *const ifinfomsg) -> *const rtattr {
|
||||||
|
return (r as *const u8).offset(nlmsg_align!(std::mem::size_of::<ifinfomsg>()) as isize)
|
||||||
|
as *const rtattr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InterfaceIterator {
|
||||||
|
socket: libc::c_int,
|
||||||
|
src_addr: libc::sockaddr_nl,
|
||||||
|
dest_addr: libc::sockaddr_nl,
|
||||||
|
subscribed: bool,
|
||||||
|
|
||||||
|
buffer: [u8; 4096],
|
||||||
|
current_header: Option<*const libc::nlmsghdr>,
|
||||||
|
current_len: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_interface_iterator() -> Result<InterfaceIterator, String> {
|
||||||
|
let socket;
|
||||||
|
|
||||||
|
let mut src_addr = unsafe { std::mem::zeroed::<libc::sockaddr_nl>() };
|
||||||
|
let mut dest_addr = unsafe { std::mem::zeroed::<libc::sockaddr_nl>() };
|
||||||
|
|
||||||
|
src_addr.nl_family = libc::AF_NETLINK as u16;
|
||||||
|
src_addr.nl_groups = libc::RTMGRP_LINK as u32;
|
||||||
|
src_addr.nl_pid = unsafe { libc::getpid() } as u32;
|
||||||
|
|
||||||
|
dest_addr.nl_family = libc::AF_NETLINK as u16;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
socket = libc::socket(libc::AF_NETLINK, libc::SOCK_RAW, libc::NETLINK_ROUTE);
|
||||||
|
if socket == -1 {
|
||||||
|
return Err("Failed to create AF_NETLINK socket!".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if libc::bind(
|
||||||
|
socket,
|
||||||
|
&src_addr as *const libc::sockaddr_nl as *const libc::sockaddr,
|
||||||
|
std::mem::size_of::<libc::sockaddr>() as u32,
|
||||||
|
) == -1
|
||||||
|
{
|
||||||
|
return Err("Failed to bind to AF_NETLINK socket!".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut request = unsafe { std::mem::zeroed::<(libc::nlmsghdr, ifinfomsg)>() };
|
||||||
|
request.0.nlmsg_len = nlmsg_length!(std::mem::size_of::<ifinfomsg>());
|
||||||
|
request.0.nlmsg_type = libc::RTM_GETLINK;
|
||||||
|
request.0.nlmsg_flags = (libc::NLM_F_REQUEST | libc::NLM_F_DUMP) as u16;
|
||||||
|
request.1.ifi_family = libc::AF_NETLINK as u8;
|
||||||
|
|
||||||
|
let mut iov = libc::iovec {
|
||||||
|
iov_base: &mut request as *mut _ as *mut libc::c_void,
|
||||||
|
iov_len: request.0.nlmsg_len as usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut msg = unsafe { std::mem::zeroed::<libc::msghdr>() };
|
||||||
|
msg.msg_name = &mut dest_addr as *mut _ as *mut libc::c_void;
|
||||||
|
msg.msg_namelen = std::mem::size_of::<libc::sockaddr_nl>() as u32;
|
||||||
|
msg.msg_iov = &mut iov as *mut libc::iovec;
|
||||||
|
msg.msg_iovlen = 1;
|
||||||
|
|
||||||
|
if unsafe { libc::sendmsg(socket, &msg as *const libc::msghdr, 0) } < 0 {
|
||||||
|
return Err("Failed to send interface request!".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(InterfaceIterator {
|
||||||
|
socket: socket,
|
||||||
|
src_addr: src_addr,
|
||||||
|
dest_addr: dest_addr,
|
||||||
|
subscribed: true,
|
||||||
|
|
||||||
|
buffer: [0; 4096],
|
||||||
|
current_header: None,
|
||||||
|
current_len: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InterfaceDetails {
|
||||||
|
pub index: i32,
|
||||||
|
pub name: [libc::c_char; libc::IFNAMSIZ],
|
||||||
|
pub hwaddr: [u8; 6],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MessageType {
|
||||||
|
NewLink(InterfaceDetails),
|
||||||
|
DelLink(InterfaceDetails),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for InterfaceIterator {
|
||||||
|
type Item = MessageType;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
if let None = self.current_header {
|
||||||
|
let mut recv_iov = libc::iovec {
|
||||||
|
iov_base: &mut self.buffer as *mut _ as *mut libc::c_void,
|
||||||
|
iov_len: 4096,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut msg = unsafe { std::mem::zeroed::<libc::msghdr>() };
|
||||||
|
msg.msg_name = &mut self.dest_addr as *mut _ as *mut libc::c_void;
|
||||||
|
msg.msg_namelen = std::mem::size_of::<libc::sockaddr_nl>() as u32;
|
||||||
|
msg.msg_iov = &mut recv_iov as *mut libc::iovec;
|
||||||
|
msg.msg_iovlen = 1;
|
||||||
|
|
||||||
|
let n = unsafe { libc::recvmsg(self.socket, &mut msg as *mut libc::msghdr, 0) };
|
||||||
|
if n < 0 {
|
||||||
|
panic!("Failed to receive on AF_NETLINK socket!");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_header = Some(&self.buffer as *const _ as *const libc::nlmsghdr);
|
||||||
|
self.current_len = n as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if unsafe { !nlmsg_ok(self.current_header.unwrap(), &mut self.current_len) } {
|
||||||
|
self.current_header = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if unsafe { (*self.current_header.unwrap()).nlmsg_type } == libc::NLMSG_DONE as u16 {
|
||||||
|
self.current_header = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hdr = self.current_header.unwrap();
|
||||||
|
self.current_header =
|
||||||
|
Some(unsafe { nlmsg_next(self.current_header.unwrap(), &mut self.current_len) });
|
||||||
|
|
||||||
|
let ifi = unsafe { std::ptr::read(nlmsg_data(hdr) as *const ifinfomsg) };
|
||||||
|
|
||||||
|
if ifi.ifi_type != 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut addr: [u8; 6] = [0; 6];
|
||||||
|
let mut name: [libc::c_char; libc::IFNAMSIZ] = [0; libc::IFNAMSIZ];
|
||||||
|
|
||||||
|
for attr in (RtAttrs {
|
||||||
|
current_attr: unsafe { ifla_rta(nlmsg_data(hdr) as *const ifinfomsg) },
|
||||||
|
current_size: unsafe { (*hdr).nlmsg_len }
|
||||||
|
- std::mem::size_of::<libc::nlmsghdr>() as u32,
|
||||||
|
}) {
|
||||||
|
match unsafe { (*attr).rta_type } {
|
||||||
|
libc::IFLA_ADDRESS => {
|
||||||
|
addr = unsafe { std::ptr::read(rta_data(attr) as *const [u8; 6]) };
|
||||||
|
}
|
||||||
|
|
||||||
|
libc::IFLA_IFNAME => {
|
||||||
|
name = unsafe {
|
||||||
|
std::ptr::read(rta_data(attr) as *const [libc::c_char; libc::IFNAMSIZ])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let interfacedetails = InterfaceDetails {
|
||||||
|
index: ifi.ifi_index,
|
||||||
|
name: name,
|
||||||
|
hwaddr: addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
match unsafe { (*hdr).nlmsg_type } {
|
||||||
|
libc::RTM_NEWLINK => return Some(MessageType::NewLink(interfacedetails)),
|
||||||
|
libc::RTM_DELLINK => return Some(MessageType::DelLink(interfacedetails)),
|
||||||
|
_ => panic!("Unknown Message Received! {}", unsafe { (*hdr).nlmsg_type }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user