aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/error.rs6
-rw-r--r--src/main.rs25
-rw-r--r--src/util.rs34
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(|_| ())
+}