189 lines
6.5 KiB
Rust
189 lines
6.5 KiB
Rust
pub use crate::message::*;
|
|
pub use crate::monitor::*;
|
|
use serde::Deserialize;
|
|
|
|
extern crate regex;
|
|
use regex::Regex;
|
|
extern crate chrono;
|
|
use chrono::{DateTime, Local};
|
|
|
|
#[derive(Debug)]
|
|
pub struct DHCPLeases {
|
|
config: DHCPLeasesConfig,
|
|
rgx_lease: Regex,
|
|
rgx_mac: Regex,
|
|
rgx_date_start: Regex,
|
|
rgx_date_ends: Regex,
|
|
}
|
|
#[derive(Debug, PartialEq, Deserialize)]
|
|
struct DHCPLeasesConfig {
|
|
#[serde(default)]
|
|
path: String,
|
|
#[serde(default)]
|
|
mac_rules: HashMap<String, Option<Vec<String>>>,
|
|
}
|
|
|
|
impl Monitor for DHCPLeases {
|
|
fn new(
|
|
config: &HashMap<String, serde_yaml::Value>,
|
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
|
let config_node = config
|
|
.get("config")
|
|
.unwrap_or(&serde_yaml::Mapping::new().into())
|
|
.clone();
|
|
let config = serde_yaml::from_value(config_node)?;
|
|
|
|
// Regex compilation
|
|
let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap();
|
|
let rgx_mac =
|
|
Regex::new(r"(?m)^\s*hardware\s+ethernet\s([a-f0-9]{2}(?::[a-f0-9]{2}){5})\s*;")
|
|
.unwrap();
|
|
let rgx_date_start = Regex::new(r"(?m)^\s*starts\s+\d+\s+(.*?)\s*;").unwrap();
|
|
let rgx_date_ends = Regex::new(r"(?m)^\s*ends\s+\d+\s+(.*?)\s*;").unwrap();
|
|
|
|
Ok(DHCPLeases {
|
|
config: config,
|
|
rgx_lease: rgx_lease,
|
|
rgx_mac: rgx_mac,
|
|
rgx_date_start: rgx_date_start,
|
|
rgx_date_ends: rgx_date_ends,
|
|
})
|
|
}
|
|
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
|
use std::fs::File;
|
|
use std::io::prelude::*;
|
|
|
|
let config_file_path = match self.config.path.len() {
|
|
0 => "/var/lib/dhcp/dhcpd.leases",
|
|
_ => &self.config.path,
|
|
};
|
|
let mut config_file =
|
|
File::open(config_file_path).expect("Could not open DHCP leases file");
|
|
let mut config_content = String::new();
|
|
config_file
|
|
.read_to_string(&mut config_content)
|
|
.expect("Could not read DHCP leases file");
|
|
|
|
let mut unauthorized_macs: Vec<String> = vec![];
|
|
|
|
for cap in self.rgx_lease.captures_iter(&config_content) {
|
|
let _ip = cap.get(1).unwrap().as_str();
|
|
let content = cap.get(2).unwrap().as_str();
|
|
|
|
let mac = self
|
|
.rgx_mac
|
|
.captures(content)
|
|
.expect(&format!(
|
|
"No 'hardware ethernet' field found for MAC address in {}",
|
|
content
|
|
))
|
|
.get(1)
|
|
.unwrap()
|
|
.as_str();
|
|
let starts_str = self
|
|
.rgx_date_start
|
|
.captures(content)
|
|
.expect("No 'starts' field found in lease")
|
|
.get(1)
|
|
.unwrap()
|
|
.as_str();
|
|
let starts =
|
|
chrono::naive::NaiveDateTime::parse_from_str(starts_str, "%Y/%m/%d %H:%M:%S")
|
|
.expect(&format!("Bad date format: '{}'", starts_str));
|
|
let ends_str = self
|
|
.rgx_date_ends
|
|
.captures(content)
|
|
.expect("No 'ends' field found in lease")
|
|
.get(1)
|
|
.unwrap()
|
|
.as_str();
|
|
let ends = chrono::naive::NaiveDateTime::parse_from_str(ends_str, "%Y/%m/%d %H:%M:%S")
|
|
.expect(&format!("Bad date format: '{}'", ends_str));
|
|
|
|
let now = Local::now().naive_local();
|
|
if starts <= now && now < ends {
|
|
// Lease is active
|
|
if let Some(rules) = self.config.mac_rules.get(mac) {
|
|
// Found rules
|
|
if let Some(rules_list) = rules {
|
|
// Rules contains one or more entries
|
|
for rule in rules_list {
|
|
if content.find(rule).is_none() {
|
|
unauthorized_macs.push(mac.to_owned());
|
|
|
|
sender
|
|
.send(Message {
|
|
emitter: "dhcp_leases".to_owned(),
|
|
level: Level::Issue,
|
|
msg_type: "dhcp_leases.unauthorized_mac.rule".to_owned(),
|
|
text: format!(
|
|
"Mismatching rule '{}' for device {}",
|
|
rule, mac
|
|
),
|
|
})
|
|
.unwrap();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
unauthorized_macs.push(mac.to_owned());
|
|
|
|
sender
|
|
.send(Message {
|
|
emitter: "dhcp_leases".to_owned(),
|
|
level: Level::Issue,
|
|
msg_type: "dhcp_leases.unauthorized_mac.unknown".to_owned(),
|
|
text: format!("Unauthorized device on network: {}", mac),
|
|
})
|
|
.unwrap();
|
|
}
|
|
}
|
|
}
|
|
|
|
if unauthorized_macs.len() > 0 {
|
|
sender
|
|
.send(Message {
|
|
emitter: "dhcp_leases".to_owned(),
|
|
level: Level::Issue,
|
|
msg_type: "dhcp_leases.unknown_mac".to_owned(),
|
|
text: format!(
|
|
"The following macs are not allowed: {:?}",
|
|
unauthorized_macs
|
|
),
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
// let leases: Vec<(&str, &str)> = lease_rgx
|
|
// .captures_iter(&config_content)
|
|
// .map(|c| (c.get(1).unwrap().as_str(), c.get(2).unwrap().as_str()))
|
|
// .collect();
|
|
// println!("{:?}", leases);
|
|
// let cap = lease_rgx.captures(config_content);
|
|
|
|
// sender
|
|
// .send(Message {
|
|
// emitter: "dhcp_leases".to_owned(),
|
|
// level: 10,
|
|
// msg_type: "string".to_owned(),
|
|
// text: format!("frfr"),
|
|
// })
|
|
// .unwrap();
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(2000));
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
lease 192.168.0.26 {
|
|
starts 4 2018/08/16 22:31:22;
|
|
ends 4 2018/08/16 23:01:22;
|
|
tstp 4 2018/08/16 23:01:22;
|
|
cltt 4 2018/08/16 22:31:22;
|
|
binding state free;
|
|
hardware ethernet 84:4b:f5:16:f4:94;
|
|
}
|
|
*/
|