aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeerserverDE <himbeerserverde@gmail.com>2023-03-04 17:56:09 +0100
committerHimbeerserverDE <himbeerserverde@gmail.com>2023-03-04 17:56:09 +0100
commit0949b26177418518aaa61ea488cae7c2e6183cc8 (patch)
treef69fb54f1eca7b9ac4e3d79577e216518a0978f5
parentea80252f062eb0d825f4ad49fb80e16578391420 (diff)
add LeaseFileManager
leases can now be saved on persistent storage and are preserved across restarts
-rw-r--r--Cargo.lock43
-rw-r--r--Cargo.toml3
-rw-r--r--src/error.rs2
-rw-r--r--src/lease.rs145
-rw-r--r--src/main.rs32
5 files changed, 208 insertions, 17 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 387b35e..c6ef03c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -63,6 +63,9 @@ dependencies = [
"ipnet",
"linkaddrs",
"rand",
+ "serde",
+ "serde_derive",
+ "serde_json",
"socket2 0.5.1",
"thiserror",
]
@@ -250,6 +253,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -487,6 +496,40 @@ dependencies = [
]
[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "serde"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
name = "slab"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index e5d78b8..5ced68c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,5 +10,8 @@ dhcproto = "0.8.0"
ipnet = "2.7.1"
linkaddrs = { git = "https://github.com/HimbeerserverDE/linkaddrs.git", version = "0.1.0" }
rand = "0.8.5"
+serde = "1.0"
+serde_derive = "1.0"
+serde_json = "1.0"
socket2 = "0.5.1"
thiserror = "1.0"
diff --git a/src/error.rs b/src/error.rs
index d6bf018..43378a8 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -29,6 +29,8 @@ pub enum Error {
Io(#[from] io::Error),
#[error("linkaddrs error")]
LinkAddrs(#[from] linkaddrs::Error),
+ #[error("serde_json error")]
+ SerdeJson(#[from] serde_json::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
diff --git a/src/lease.rs b/src/lease.rs
index 0369a72..4996f99 100644
--- a/src/lease.rs
+++ b/src/lease.rs
@@ -1,9 +1,14 @@
+use crate::error::Result;
+
+use std::fs::File;
+use std::io::Seek;
use std::net::Ipv4Addr;
use std::time::{Duration, SystemTime};
use ipnet::Ipv4AddrRange;
+use serde_derive::{Deserialize, Serialize};
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Lease {
pub address: Ipv4Addr,
pub expires: SystemTime,
@@ -25,10 +30,10 @@ impl Lease {
pub trait LeaseManager {
fn range(&self) -> (Ipv4Addr, Ipv4Addr);
fn netmask(&self) -> Ipv4Addr;
- fn leases(&self) -> Box<dyn Iterator<Item = Lease>>;
- fn request(&mut self, address: Ipv4Addr, client_id: &[u8]) -> bool;
fn lease_time(&self) -> Duration;
- fn release(&mut self, client_id: &[u8]) -> Box<dyn Iterator<Item = Ipv4Addr>>;
+ fn leases(&self) -> Box<dyn Iterator<Item = Lease>>;
+ fn request(&mut self, address: Ipv4Addr, client_id: &[u8]) -> Result<bool>;
+ fn release(&mut self, client_id: &[u8]) -> Result<Box<dyn Iterator<Item = Ipv4Addr>>>;
fn all_addresses(&self) -> Vec<Ipv4Addr> {
let range = self.range();
@@ -137,6 +142,10 @@ impl LeaseManager for LeaseDummyManager {
"255.255.255.0".parse().unwrap()
}
+ fn lease_time(&self) -> Duration {
+ Duration::from_secs(300)
+ }
+
fn leases(&self) -> Box<dyn Iterator<Item = Lease>> {
Box::new(
self.leases
@@ -146,9 +155,9 @@ impl LeaseManager for LeaseDummyManager {
)
}
- fn request(&mut self, address: Ipv4Addr, client_id: &[u8]) -> bool {
+ fn request(&mut self, address: Ipv4Addr, client_id: &[u8]) -> Result<bool> {
if self.is_unavailable(address, client_id) {
- false
+ Ok(false)
} else {
let lease = self
.leases
@@ -163,15 +172,130 @@ impl LeaseManager for LeaseDummyManager {
self.leases
.push(Lease::new(address, self.lease_time(), client_id.to_vec()));
- true
+ Ok(true)
}
}
+ fn release(&mut self, client_id: &[u8]) -> Result<Box<dyn Iterator<Item = Ipv4Addr>>> {
+ let mut released = Vec::new();
+
+ self.leases
+ .clone()
+ .into_iter()
+ .enumerate()
+ .filter(|(_, lease)| lease.client_id == client_id)
+ .for_each(|(i, lease)| {
+ self.leases.remove(i);
+ released.push(lease.address);
+ });
+
+ Ok(Box::new(released.into_iter()))
+ }
+}
+
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+pub struct LeaseFileManagerConfig {
+ pub range: (Ipv4Addr, Ipv4Addr),
+ pub netmask: Ipv4Addr,
+ pub lease_time: Duration,
+}
+
+#[derive(Debug)]
+pub struct LeaseFileManager {
+ config: LeaseFileManagerConfig,
+ file: File,
+ leases: Vec<Lease>,
+}
+
+impl LeaseFileManager {
+ pub fn new(config: LeaseFileManagerConfig, file: File) -> Result<Self> {
+ let mut mgr = Self {
+ config,
+ file,
+ leases: Vec::new(),
+ };
+
+ mgr.load()?;
+ Ok(mgr)
+ }
+
+ fn load(&mut self) -> Result<()> {
+ self.file.rewind()?;
+ self.leases = serde_json::from_reader(&self.file)?;
+
+ Ok(())
+ }
+
+ fn save(&mut self) -> Result<()> {
+ self.file.rewind()?;
+ self.file.set_len(0)?;
+
+ serde_json::to_writer_pretty(&self.file, &self.leases)?;
+ Ok(())
+ }
+
+ fn garbage_collect(&mut self) -> Result<()> {
+ self.leases = self
+ .leases
+ .clone()
+ .into_iter()
+ .filter(|lease| SystemTime::now().duration_since(lease.expires).is_err())
+ .collect();
+
+ self.save()?;
+ Ok(())
+ }
+}
+
+impl LeaseManager for LeaseFileManager {
+ fn range(&self) -> (Ipv4Addr, Ipv4Addr) {
+ self.config.range
+ }
+
+ fn netmask(&self) -> Ipv4Addr {
+ self.config.netmask
+ }
+
fn lease_time(&self) -> Duration {
- Duration::from_secs(300)
+ self.config.lease_time
+ }
+
+ fn leases(&self) -> Box<dyn Iterator<Item = Lease>> {
+ Box::new(
+ self.leases
+ .clone()
+ .into_iter()
+ .filter(|lease| SystemTime::now().duration_since(lease.expires).is_err()),
+ )
}
- fn release(&mut self, client_id: &[u8]) -> Box<dyn Iterator<Item = Ipv4Addr>> {
+ fn request(&mut self, address: Ipv4Addr, client_id: &[u8]) -> Result<bool> {
+ self.garbage_collect()?;
+
+ if self.is_unavailable(address, client_id) {
+ Ok(false)
+ } else {
+ let lease = self
+ .leases
+ .iter()
+ .enumerate()
+ .find(|(_, lease)| lease.client_id == client_id);
+
+ if let Some(lease) = lease {
+ self.leases.remove(lease.0);
+ }
+
+ self.leases
+ .push(Lease::new(address, self.lease_time(), client_id.to_vec()));
+
+ self.save()?;
+ Ok(true)
+ }
+ }
+
+ fn release(&mut self, client_id: &[u8]) -> Result<Box<dyn Iterator<Item = Ipv4Addr>>> {
+ self.garbage_collect()?;
+
let mut released = Vec::new();
self.leases
@@ -184,6 +308,7 @@ impl LeaseManager for LeaseDummyManager {
released.push(lease.address);
});
- Box::new(released.into_iter())
+ self.save()?;
+ Ok(Box::new(released.into_iter()))
}
}
diff --git a/src/main.rs b/src/main.rs
index 99d467a..694a4f7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,18 +1,36 @@
use dhcp4d::error::{Error, Result};
-use dhcp4d::lease::{LeaseDummyManager, LeaseManager};
+use dhcp4d::lease::{LeaseFileManager, LeaseFileManagerConfig, LeaseManager};
use dhcp4d::util::{format_client_id, local_ip};
+use std::fs::OpenOptions;
use std::mem::MaybeUninit;
use std::net::{IpAddr, SocketAddr, SocketAddrV4};
use std::sync::{Arc, Mutex};
use std::thread;
+use std::time::Duration;
use dhcproto::v4::{DhcpOption, Flags, Message, MessageType, Opcode, OptionCode};
use dhcproto::{Decodable, Decoder, Encodable, Encoder};
use socket2::{Domain, Socket, Type};
fn main() -> Result<()> {
- let lease_mgr = Arc::new(Mutex::new(LeaseDummyManager::new(None)));
+ let config = LeaseFileManagerConfig {
+ range: (
+ "198.51.100.100".parse().unwrap(),
+ "198.51.100.249".parse().unwrap(),
+ ),
+ netmask: "255.255.255.0".parse().unwrap(),
+ lease_time: Duration::from_secs(300),
+ };
+
+ let file = OpenOptions::new()
+ .create(true)
+ .read(true)
+ .write(true)
+ .truncate(false)
+ .open("leases.json")?;
+
+ let lease_mgr = Arc::new(Mutex::new(LeaseFileManager::new(config, file)?));
let mut threads = Vec::new();
for arg in std::env::args().skip(1) {
@@ -27,7 +45,7 @@ fn main() -> Result<()> {
Ok(())
}
-fn run(link: String, lease_mgr: Arc<Mutex<LeaseDummyManager>>) -> 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)?;
@@ -56,9 +74,9 @@ fn run(link: String, lease_mgr: Arc<Mutex<LeaseDummyManager>>) -> Result<()> {
}
}
-fn handle_request(
+fn handle_request<T: LeaseManager>(
sock: &Socket,
- lease_mgr: Arc<Mutex<LeaseDummyManager>>,
+ lease_mgr: Arc<Mutex<T>>,
buf: &[u8],
remote: SocketAddrV4,
) -> Result<()> {
@@ -144,7 +162,7 @@ fn handle_request(
_ => unreachable!(),
};
- if !lease_mgr.request(*requested_addr, client_id) {
+ if !lease_mgr.request(*requested_addr, client_id)? {
let own_addr = local_ip(sock);
let mut resp = Message::default();
@@ -225,7 +243,7 @@ fn handle_request(
let mut lease_mgr = lease_mgr.lock().unwrap();
let released: Vec<String> = lease_mgr
- .release(client_id)
+ .release(client_id)?
.map(|addr| addr.to_string())
.collect();