use derive_builder::Builder;
use pnet::util::MacAddr;
use std::{
net::Ipv4Addr,
sync::{Arc, Mutex, mpsc},
thread::{self, JoinHandle},
time::Duration,
};
use crate::{
error::Result, packet::heartbeat_packet::HeartbeatPacketBuilder,
wire::Sender,
};
#[derive(Builder)]
pub struct HeartBeat {
source_mac: MacAddr,
source_ipv4: Ipv4Addr,
source_port: u16,
packet_sender: Arc<Mutex<dyn Sender>>,
}
impl HeartBeat {
pub fn builder() -> HeartBeatBuilder {
HeartBeatBuilder::default()
}
pub fn beat(&self) -> Result<()> {
let heartbeat_packet = HeartbeatPacketBuilder::default()
.source_ip(self.source_ipv4)
.source_mac(self.source_mac)
.source_port(self.source_port)
.build()?;
let packet = heartbeat_packet.to_raw();
let mut sender = self.packet_sender.lock()?;
sender.send(&packet)?;
Ok(())
}
pub fn start_in_thread(
&self,
done: mpsc::Receiver<()>,
) -> Result<JoinHandle<Result<()>>> {
let source_mac = self.source_mac;
let source_ipv4 = self.source_ipv4;
let source_port = self.source_port;
let packet_sender = Arc::clone(&self.packet_sender);
let heartbeat_packet = HeartbeatPacketBuilder::default()
.source_ip(source_ipv4)
.source_mac(source_mac)
.source_port(source_port)
.build()?;
let packet = heartbeat_packet.to_raw();
Ok(thread::spawn(move || -> Result<()> {
log::debug!("starting heartbeat thread");
let mut misses = 0;
let mut send_err = None;
let interval = Duration::from_secs(1);
loop {
if misses >= 5
&& let Some(err) = send_err.take()
{
return Err(err);
}
if done.try_recv().is_ok() {
log::debug!("stopping heartbeat");
return Ok(());
}
log::debug!("sending heartbeat");
{
let mut sender = packet_sender.lock()?;
if let Err(err) = sender.send(&packet) {
misses += 1;
send_err = Some(err);
} else {
misses = 0;
send_err = None;
}
}
thread::sleep(interval);
}
}))
}
}
#[cfg(test)]
#[path = "./heartbeat_tests.rs"]
mod tests;