summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMat Wood <thepacketgeek@users.noreply.github.com>2020-09-29 08:32:58 -0700
committerGitHub <noreply@github.com>2020-09-29 17:32:58 +0200
commit0737704d4e62bb3065a69d401e88e7bfc12f31c0 (patch)
tree4d2112cd60a74b006c94361c6aed502dbab26ea5
parentf9d397c7c90a5e262428fe909d44f1d81f0e88c6 (diff)
fix(sniffer): handle disconnected interfaces (and periodically attempt to re-connect) (#191)
* Panic on non-Timeout errors in packet Sniffer * Attempt to Re-establish interface channels after disconnect Sniffer::next() now returns an io::Result so the thread in main can determine between no packets & a sniffing error - Matching on EtherType to remove duplicate code determining IP Version Added Sniffer::reset_channel to allow main to poll a previously connected interface * Error handling & timeout delay performed in Sniffer::next * Removing no longer needed Ether layer in test packet builder
-rw-r--r--src/network/sniffer.rs26
-rw-r--r--src/os/mod.rs2
-rw-r--r--src/os/shared.rs2
3 files changed, 27 insertions, 3 deletions
diff --git a/src/network/sniffer.rs b/src/network/sniffer.rs
index 52014b8..de587be 100644
--- a/src/network/sniffer.rs
+++ b/src/network/sniffer.rs
@@ -10,9 +10,15 @@ use ::pnet::packet::udp::UdpPacket;
use ::pnet::packet::Packet;
use ::ipnetwork::IpNetwork;
+use ::std::io::{self, Result};
use ::std::net::{IpAddr, SocketAddr};
+use ::std::thread::park_timeout;
use crate::network::{Connection, Protocol};
+use crate::os::shared::get_datalink_channel;
+
+const PACKET_WAIT_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(10);
+const CHANNEL_RESET_DELAY: std::time::Duration = std::time::Duration::from_millis(1000);
#[derive(Debug)]
pub struct Segment {
@@ -96,7 +102,20 @@ impl Sniffer {
}
}
pub fn next(&mut self) -> Option<Segment> {
- let bytes = self.network_frames.next().ok()?;
+ let bytes = match self.network_frames.next() {
+ Ok(bytes) => bytes,
+ Err(err) => match err.kind() {
+ std::io::ErrorKind::TimedOut => {
+ park_timeout(PACKET_WAIT_TIMEOUT);
+ return None;
+ }
+ _ => {
+ park_timeout(CHANNEL_RESET_DELAY);
+ self.reset_channel().ok();
+ return None;
+ }
+ },
+ };
// See https://github.com/libpnet/libpnet/blob/master/examples/packetdump.rs
// VPN interfaces (such as utun0, utun1, etc) have POINT_TO_POINT bit set to 1
let payload_offset = if (self.network_interface.is_loopback()
@@ -133,6 +152,11 @@ impl Sniffer {
}
}
}
+ pub fn reset_channel(&mut self) -> Result<()> {
+ self.network_frames = get_datalink_channel(&self.network_interface)
+ .map_err(|_| io::Error::new(io::ErrorKind::Other, "Interface not available"))?;
+ Ok(())
+ }
fn handle_v6(ip_packet: Ipv6Packet, network_interface: &NetworkInterface) -> Option<Segment> {
let (protocol, source_port, destination_port, data_length) =
extract_transport_protocol!(ip_packet);
diff --git a/src/os/mod.rs b/src/os/mod.rs
index ec04ecd..4db5b0a 100644
--- a/src/os/mod.rs
+++ b/src/os/mod.rs
@@ -11,6 +11,6 @@ mod lsof_utils;
pub(self) mod windows;
mod errors;
-mod shared;
+pub(crate) mod shared;
pub use shared::*;
diff --git a/src/os/shared.rs b/src/os/shared.rs
index 6731531..3c0fe74 100644
--- a/src/os/shared.rs
+++ b/src/os/shared.rs
@@ -31,7 +31,7 @@ impl Iterator for TerminalEvents {
}
}
-fn get_datalink_channel(
+pub(crate) fn get_datalink_channel(
interface: &NetworkInterface,
) -> Result<Box<dyn DataLinkReceiver>, GetInterfaceErrorKind> {
let mut config = Config::default();