use std::collections::VecDeque;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use flowscope::tracker::FlowEvents;
use flowscope::{
FlowDatagramDriver, FlowEvent, FlowExtractor, FlowSessionDriver, FlowTracker,
FlowTrackerConfig, PacketView, SessionEvent, Timestamp,
};
use futures_core::Stream;
use crate::error::Error;
use crate::pcap_source::AsyncPcapSource;
pub struct PcapFlowStream<E>
where
E: FlowExtractor,
{
source: AsyncPcapSource,
tracker: FlowTracker<E, ()>,
pending: VecDeque<FlowEvent<E::Key>>,
eof: bool,
}
impl<E> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: Clone + Send + 'static,
{
pub(crate) fn new(source: AsyncPcapSource, extractor: E) -> Self {
Self {
source,
tracker: FlowTracker::new(extractor),
pending: VecDeque::new(),
eof: false,
}
}
pub fn with_config(mut self, config: FlowTrackerConfig) -> Self {
self.tracker.set_config(config);
self
}
pub fn with_idle_timeout_fn<G>(mut self, f: G) -> Self
where
G: Fn(&E::Key, Option<flowscope::L4Proto>) -> Option<Duration> + Send + Sync + 'static,
{
self.tracker.set_idle_timeout_fn(f);
self
}
pub fn tracker(&self) -> &FlowTracker<E, ()> {
&self.tracker
}
pub fn tracker_stats(&self) -> &flowscope::FlowTrackerStats {
self.tracker.stats()
}
pub fn active_flows(&self) -> usize {
self.tracker.flows().count()
}
pub fn packets_read(&self) -> u64 {
self.source.packets_yielded()
}
}
impl<E> Stream for PcapFlowStream<E>
where
E: FlowExtractor + Unpin,
E::Key: Clone + Unpin,
{
type Item = Result<FlowEvent<E::Key>, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(evt) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(evt)));
}
if this.eof {
for ev in this.tracker.sweep(Timestamp::MAX) {
this.pending.push_back(ev);
}
if let Some(evt) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(evt)));
}
return Poll::Ready(None);
}
match Pin::new(&mut this.source).poll_next(cx) {
Poll::Ready(Some(Ok(owned))) => {
let view = PacketView::new(&owned.data, owned.timestamp);
let evts: FlowEvents<E::Key> = this.tracker.track(view);
for ev in evts {
this.pending.push_back(ev);
}
}
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
Poll::Ready(None) => {
this.eof = true;
}
Poll::Pending => return Poll::Pending,
}
}
}
}
impl AsyncPcapSource {
pub fn flow_events<E>(self, extractor: E) -> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: Clone + Send + 'static,
{
PcapFlowStream::new(self, extractor)
}
pub fn sessions<E, P>(self, extractor: E, parser: P) -> PcapSessionStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: flowscope::SessionParser + Clone + Send + Sync,
{
PcapSessionStream::new(self, FlowSessionDriver::new(extractor, parser))
}
pub fn datagrams<E, P>(self, extractor: E, parser: P) -> PcapDatagramStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: flowscope::DatagramParser + Clone + Send + Sync,
{
PcapDatagramStream::new(self, FlowDatagramDriver::new(extractor, parser))
}
}
impl<E> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
{
pub fn session_stream<P>(self, parser: P) -> PcapSessionStream<E, P>
where
P: flowscope::SessionParser + Clone + Send + Sync,
{
let config = self.tracker.config().clone();
let extractor = self.tracker.into_extractor();
PcapSessionStream::new(
self.source,
FlowSessionDriver::with_config(extractor, parser, config),
)
}
pub fn datagram_stream<P>(self, parser: P) -> PcapDatagramStream<E, P>
where
P: flowscope::DatagramParser + Clone + Send + Sync,
{
let config = self.tracker.config().clone();
let extractor = self.tracker.into_extractor();
PcapDatagramStream::new(
self.source,
FlowDatagramDriver::with_config(extractor, parser, config),
)
}
}
pub struct PcapSessionStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: flowscope::SessionParser + Clone + Send + Sync,
{
source: AsyncPcapSource,
driver: FlowSessionDriver<E, P>,
pending: VecDeque<SessionEvent<E::Key, <P as flowscope::SessionParser>::Message>>,
finished: bool,
}
impl<E, P> PcapSessionStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: flowscope::SessionParser + Clone + Send + Sync,
{
pub(crate) fn new(source: AsyncPcapSource, driver: FlowSessionDriver<E, P>) -> Self {
Self {
source,
driver,
pending: VecDeque::new(),
finished: false,
}
}
pub fn driver(&self) -> &FlowSessionDriver<E, P> {
&self.driver
}
pub fn tracker_stats(&self) -> &flowscope::FlowTrackerStats {
self.driver.tracker().stats()
}
pub fn active_flows(&self) -> usize {
self.driver.tracker().flows().count()
}
pub fn packets_read(&self) -> u64 {
self.source.packets_yielded()
}
}
impl<E, P> Stream for PcapSessionStream<E, P>
where
E: FlowExtractor + Unpin,
E::Key: std::hash::Hash + Eq + Clone + Send + Unpin + 'static,
P: flowscope::SessionParser + Clone + Send + Sync + Unpin + Send + Sync,
<P as flowscope::SessionParser>::Message: Unpin,
{
type Item = Result<SessionEvent<E::Key, <P as flowscope::SessionParser>::Message>, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(ev) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(ev)));
}
if this.finished {
return Poll::Ready(None);
}
match Pin::new(&mut this.source).poll_next(cx) {
Poll::Ready(Some(Ok(owned))) => {
let view = PacketView::new(&owned.data, owned.timestamp);
for ev in this.driver.track(view) {
this.pending.push_back(ev);
}
}
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
Poll::Ready(None) => {
for ev in this.driver.finish() {
this.pending.push_back(ev);
}
this.finished = true;
}
Poll::Pending => return Poll::Pending,
}
}
}
}
pub struct PcapDatagramStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: flowscope::DatagramParser + Clone + Send + Sync,
{
source: AsyncPcapSource,
driver: FlowDatagramDriver<E, P>,
pending: VecDeque<SessionEvent<E::Key, <P as flowscope::DatagramParser>::Message>>,
finished: bool,
}
impl<E, P> PcapDatagramStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: flowscope::DatagramParser + Clone + Send + Sync,
{
pub(crate) fn new(source: AsyncPcapSource, driver: FlowDatagramDriver<E, P>) -> Self {
Self {
source,
driver,
pending: VecDeque::new(),
finished: false,
}
}
pub fn driver(&self) -> &FlowDatagramDriver<E, P> {
&self.driver
}
pub fn tracker_stats(&self) -> &flowscope::FlowTrackerStats {
self.driver.tracker().stats()
}
pub fn active_flows(&self) -> usize {
self.driver.tracker().flows().count()
}
pub fn packets_read(&self) -> u64 {
self.source.packets_yielded()
}
}
impl<E, P> Stream for PcapDatagramStream<E, P>
where
E: FlowExtractor + Unpin,
E::Key: std::hash::Hash + Eq + Clone + Send + Unpin + 'static,
P: flowscope::DatagramParser + Clone + Send + Sync + Unpin + Send + Sync,
<P as flowscope::DatagramParser>::Message: Unpin,
{
type Item = Result<SessionEvent<E::Key, <P as flowscope::DatagramParser>::Message>, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(ev) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(ev)));
}
if this.finished {
return Poll::Ready(None);
}
match Pin::new(&mut this.source).poll_next(cx) {
Poll::Ready(Some(Ok(owned))) => {
let view = PacketView::new(&owned.data, owned.timestamp);
for ev in this.driver.track(view) {
this.pending.push_back(ev);
}
}
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
Poll::Ready(None) => {
for ev in this.driver.finish() {
this.pending.push_back(ev);
}
this.finished = true;
}
Poll::Pending => return Poll::Pending,
}
}
}
}