rnetmon/src/plugins/monitor/dhcp_leases.rs

157 lines
5.3 KiB
Rust

pub use crate::message::*;
pub use crate::monitor::*;
use serde_derive::*;
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, Serialize, Deserialize)]
struct DHCPLeasesConfig {
path: String,
mac_rules: HashMap<String, Vec<String>>,
}
impl Monitor for DHCPLeases {
fn new(config: &HashMap<String, serde_yaml::Value>) -> Self {
let config_node = config.get("config").expect("Missing `config` key").clone();
let config =
serde_yaml::from_value(config_node).expect("Invalid config for wifi_availability");
// Regex compilation
let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap();
let rgx_mac =
Regex::new(r"^\s*hardware\s+ethernet\s([a-f0-9]{2}(?:\:[a-f0-9]{2}){5})\s*;").unwrap();
let rgx_date_start = Regex::new(r"^\s*starts\s+(.*?)\s*;").unwrap();
let rgx_date_ends = Regex::new(r"^\s*ends\s+(.*?)\s*;").unwrap();
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 mut config_file =
File::open(&self.config.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("No 'hardware ethernet' field found for MAC address")
.get(0)
.unwrap()
.as_str();
let starts_str = self
.rgx_date_start
.captures(content)
.expect("No 'starts' field found in lease")
.get(0)
.unwrap()
.as_str();
let starts =
chrono::naive::NaiveDateTime::parse_from_str(starts_str, "%U %Y/%m/%d %H:%M:%S")
.expect("Bad date format");
let ends_str = self
.rgx_date_ends
.captures(content)
.expect("No 'ends' field found in lease")
.get(0)
.unwrap()
.as_str();
let ends =
chrono::naive::NaiveDateTime::parse_from_str(ends_str, "%U %Y/%m/%d %H:%M:%S")
.expect("Bad date format");
let now = Local::now().naive_local();
if starts <= now && now < ends {
// Lease is active
if let Some(rules) = self.config.mac_rules.get(mac) {
for rule in rules {
if content.find(rule).is_none() {
unauthorized_macs.push(mac.to_owned());
sender
.send(Message {
emitter: "dhcp_leases".to_owned(),
level: Level::Error,
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::Error,
msg_type: "dhcp_leases.unauthorized_mac.unknown".to_owned(),
text: format!("Unauthorized device on network: {}", mac),
})
.unwrap();
}
}
}
if unauthorized_macs.len() > 0 {}
// 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;
}
*/