diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/error.rs | 6 | ||||
-rw-r--r-- | src/main.rs | 25 | ||||
-rw-r--r-- | src/util.rs | 34 |
3 files changed, 58 insertions, 7 deletions
diff --git a/src/error.rs b/src/error.rs index 43378a8..d97212b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,6 @@ +use std::ffi; use std::io; +use std::net; use dhcproto::v4::{MessageType, Opcode}; use thiserror::Error; @@ -31,6 +33,10 @@ pub enum Error { LinkAddrs(#[from] linkaddrs::Error), #[error("serde_json error")] SerdeJson(#[from] serde_json::Error), + #[error("error parsing IP address")] + AddrParseError(#[from] net::AddrParseError), + #[error("ffi nul error (string contains nul bytes)")] + FfiNulError(#[from] ffi::NulError), } pub type Result<T> = std::result::Result<T, Error>; diff --git a/src/main.rs b/src/main.rs index 4ef4b23..1ef444c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ use dhcp4d::error::{Error, Result}; use dhcp4d::lease::{LeaseFileManager, LeaseFileManagerConfig, LeaseManager}; -use dhcp4d::util::{format_client_id, local_ip}; +use dhcp4d::util::{format_client_id, local_ip, setsockopt}; +use std::ffi::CString; use std::fs::{self, OpenOptions}; use std::io::Write; use std::mem::MaybeUninit; -use std::net::{IpAddr, SocketAddr, SocketAddrV4}; +use std::net::{SocketAddr, SocketAddrV4}; +use std::os::fd::AsRawFd; +use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; @@ -60,13 +63,23 @@ fn main() -> Result<()> { fn run<T: LeaseManager>(link: String, lease_mgr: Arc<Mutex<T>>) -> Result<()> { let sock = Socket::new(Domain::IPV4, Type::DGRAM, None)?; - let addresses = linkaddrs::ipv4_addresses(link)?; - let address = addresses.first().expect("interface has no IPv4 addresses"); - - let address = SocketAddr::new(IpAddr::V4(address.addr()), 67); + let address = SocketAddr::from_str("0.0.0.0:67")?; sock.bind(&address.into())?; sock.set_broadcast(true)?; + sock.set_reuse_port(true)?; + + // Bind socket to interface. + unsafe { + let link_index = libc::if_nametoindex(CString::new(link)?.into_raw()); + + setsockopt( + sock.as_raw_fd(), + libc::IPPROTO_IP, + libc::SO_BINDTODEVICE, + link_index, + )?; + } loop { let mut buf = [MaybeUninit::new(0); 1024]; diff --git a/src/util.rs b/src/util.rs index 81e4fce..dfacaad 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,25 @@ use crate::error::{Error, Result}; -use socket2::Socket; + +use std::ffi::c_int; +use std::io; +use std::mem; use std::net::Ipv4Addr; +use std::ptr; + +use socket2::Socket; + +/// Helper macro to execute a system call that returns an `io::Result`. +macro_rules! syscall { + ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{ + #[allow(unused_unsafe)] + let res = unsafe { libc::$fn($($arg, )*) }; + if res == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(res) + } + }}; +} pub fn format_client_id(client_id: &[u8]) -> Result<String> { client_id @@ -14,3 +33,16 @@ pub fn local_ip(sock: &Socket) -> Ipv4Addr { let local_addr = sock.local_addr().unwrap().as_socket_ipv4().unwrap(); *local_addr.ip() } + +#[allow(clippy::missing_safety_doc)] +pub unsafe fn setsockopt<T>(fd: c_int, opt: c_int, val: c_int, payload: T) -> io::Result<()> { + let payload = ptr::addr_of!(payload).cast(); + syscall!(setsockopt( + fd, + opt, + val, + payload, + mem::size_of::<T>() as libc::socklen_t + )) + .map(|_| ()) +} |