use crate::async_adapters::tokio_adapter::AsyncCapture;
use crate::config::BpfFilter;
use crate::config::{FanoutFlags, FanoutMode};
use crate::error::Error;
use crate::{Capture, CaptureBuilder};
pub struct AsyncMultiCapture {
captures: Vec<AsyncCapture<Capture>>,
labels: Vec<String>,
}
impl AsyncMultiCapture {
pub fn open<I, S>(interfaces: I) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut captures = Vec::new();
let mut labels = Vec::new();
for iface in interfaces {
let name = iface.as_ref();
captures.push(AsyncCapture::open(name)?);
labels.push(name.to_string());
}
Self::validate_nonempty(&captures)?;
Ok(Self { captures, labels })
}
pub fn open_with_filter<I, S>(interfaces: I, filter: BpfFilter) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut captures = Vec::new();
let mut labels = Vec::new();
for iface in interfaces {
let name = iface.as_ref();
let rx = CaptureBuilder::default()
.interface(name)
.bpf_filter(filter.clone())
.build()?;
captures.push(AsyncCapture::new(rx)?);
labels.push(name.to_string());
}
Self::validate_nonempty(&captures)?;
Ok(Self { captures, labels })
}
pub fn open_workers(interface: &str, n: usize, group_id: u16) -> Result<Self, Error> {
Self::open_workers_with_mode(interface, n, group_id, FanoutMode::Cpu)
}
pub fn open_workers_with_mode(
interface: &str,
n: usize,
group_id: u16,
mode: FanoutMode,
) -> Result<Self, Error> {
if n == 0 {
return Err(Error::Config(
"AsyncMultiCapture::open_workers requires n >= 1".into(),
));
}
let mut captures = Vec::with_capacity(n);
let mut labels = Vec::with_capacity(n);
for i in 0..n {
let rx = CaptureBuilder::default()
.interface(interface)
.fanout(mode, group_id)
.fanout_flags(FanoutFlags::ROLLOVER)
.build()?;
captures.push(AsyncCapture::new(rx)?);
labels.push(format!("worker-{i}"));
}
Ok(Self { captures, labels })
}
pub fn from_captures(
captures: Vec<AsyncCapture<Capture>>,
labels: Option<Vec<String>>,
) -> Result<Self, Error> {
Self::validate_nonempty(&captures)?;
let labels = match labels {
Some(l) => {
if l.len() != captures.len() {
return Err(Error::Config(format!(
"AsyncMultiCapture::from_captures: labels.len() == {} but captures.len() == {}",
l.len(),
captures.len()
)));
}
l
}
None => (0..captures.len()).map(|i| format!("source-{i}")).collect(),
};
Ok(Self { captures, labels })
}
pub fn len(&self) -> usize {
self.captures.len()
}
pub fn is_empty(&self) -> bool {
self.captures.is_empty()
}
pub fn label(&self, i: usize) -> Option<&str> {
self.labels.get(i).map(|s| s.as_str())
}
pub fn captures(&self) -> &[AsyncCapture<Capture>] {
&self.captures
}
pub fn into_captures(self) -> (Vec<AsyncCapture<Capture>>, Vec<String>) {
(self.captures, self.labels)
}
fn validate_nonempty<T>(items: &[T]) -> Result<(), Error> {
if items.is_empty() {
Err(Error::Config(
"AsyncMultiCapture requires at least one source".into(),
))
} else {
Ok(())
}
}
}
#[cfg(all(feature = "af-xdp", feature = "xdp-loader"))]
pub struct AsyncXdpMultiCapture {
captures: Vec<crate::AsyncXdpCapture>,
labels: Vec<String>,
}
#[cfg(all(feature = "af-xdp", feature = "xdp-loader"))]
impl AsyncXdpMultiCapture {
pub fn open<I, S>(interfaces: I) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut captures = Vec::new();
let mut labels = Vec::new();
for iface in interfaces {
let name = iface.as_ref();
captures.push(crate::AsyncXdpCapture::open(name)?);
labels.push(name.to_string());
}
AsyncMultiCapture::validate_nonempty(&captures)?;
Ok(Self { captures, labels })
}
pub fn from_captures(captures: Vec<crate::AsyncXdpCapture>, labels: Vec<String>) -> Self {
Self { captures, labels }
}
pub fn len(&self) -> usize {
self.captures.len()
}
pub fn is_empty(&self) -> bool {
self.captures.is_empty()
}
pub fn label(&self, i: usize) -> Option<&str> {
self.labels.get(i).map(|s| s.as_str())
}
pub fn into_captures(self) -> (Vec<crate::AsyncXdpCapture>, Vec<String>) {
(self.captures, self.labels)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_open_errors() {
let r = AsyncMultiCapture::open(std::iter::empty::<&str>());
assert!(r.is_err());
}
#[test]
fn open_workers_zero_errors() {
let r = AsyncMultiCapture::open_workers("lo", 0, 0);
assert!(r.is_err());
}
#[test]
fn from_captures_labels_length_mismatch_errors() {
let r = AsyncMultiCapture::from_captures(Vec::new(), Some(vec!["only-one".into()]));
assert!(r.is_err());
}
}