made Ipfix part of CaptureContext (WIP)

This commit is contained in:
GyulyVGC 2026-05-15 23:01:56 +02:00
parent 55bb9f7fe5
commit 22a282bd30
11 changed files with 155 additions and 170 deletions

View File

@ -158,7 +158,7 @@ fn test_restore_default_configs() {
expanded: true,
},
import_pcap_path: "whole_day.pcapng".to_string(),
ipfix_collector: Default::default(),
ipfix_socket: Default::default(),
export_pcap: ExportPcap {
enabled: true,
file_name: "sniffnet.pcap".to_string(),

View File

@ -14,7 +14,7 @@
use crate::gui::types::filters::Filters;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::ipfix::IpfixCollectorConf;
use crate::networking::ipfix::MyIpfixSocket;
use crate::networking::types::capture_context::{CaptureSource, CaptureSourcePicklist};
use crate::networking::types::my_device::MyDevice;
use crate::networking::types::my_link_type::MyLinkType;
@ -163,7 +163,7 @@ fn get_col_data_source(sniffer: &Sniffer, language: Language) -> Column<'_, Mess
CaptureSourcePicklist::Ipfix => {
col = col.push(get_col_ipfix_collector(
language,
&sniffer.conf.ipfix_collector,
&sniffer.conf.ipfix_socket,
));
}
}
@ -319,7 +319,7 @@ fn get_col_import_pcap<'a>(
fn get_col_ipfix_collector<'a>(
language: Language,
conf: &IpfixCollectorConf,
ipfix_socket: &MyIpfixSocket,
) -> Column<'a, Message, StyleType> {
let addr_row = Row::new()
.align_y(Alignment::Center)
@ -329,8 +329,8 @@ fn get_col_ipfix_collector<'a>(
bind_address_translation(language)
)))
.push(
TextInput::new("0.0.0.0", &conf.bind_addr)
.on_input(Message::SetIpfixBindAddr)
TextInput::new("0.0.0.0", &ipfix_socket.addr())
.on_input(Message::SetIpfixAddr)
.padding([2, 5]),
);
let port_row = Row::new()
@ -338,8 +338,8 @@ fn get_col_ipfix_collector<'a>(
.spacing(5)
.push(Text::new(format!("{}:", port_translation(language))))
.push(
TextInput::new("4739", &conf.bind_port.to_string())
.on_input(Message::SetIpfixBindPort)
TextInput::new("4739", &ipfix_socket.port())
.on_input(Message::SetIpfixPort)
.padding([2, 5]),
);
let content = Column::new()

View File

@ -37,6 +37,12 @@ pub fn waiting_page(sniffer: &Sniffer) -> Option<Container<'_, Message, StyleTyp
Icon::Error.to_text().size(60),
format!("{}\n\n{error}", error_translation(language)),
)
} else if matches!(cs, CaptureSource::Ipfix(_)) {
// TODO!
(
Icon::File.to_text().size(60),
reading_from_pcap_translation(language).to_string(),
)
} else if !link_type.is_supported() {
(
Icon::Forbidden.to_text().size(60),

View File

@ -29,7 +29,6 @@
use crate::mmdb::asn::ASN_MMDB;
use crate::mmdb::country::COUNTRY_MMDB;
use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders};
use crate::networking::ipfix::MyIpfixCollector;
use crate::networking::ipfix::collect::collect_ipfix;
use crate::networking::parse_packets::BackendTrafficMessage;
use crate::networking::parse_packets::parse_packets;
@ -355,8 +354,8 @@ pub fn update(&mut self, message: Message) -> Task<Message> {
Message::ScaleFactorShortcut(increase) => self.scale_factor_shortcut(increase),
Message::SetNewerReleaseStatus(status) => self.set_newer_release_status(status),
Message::SetPcapImport(path) => self.set_pcap_import(path),
Message::SetIpfixBindAddr(addr) => self.set_ipfix_bind_addr(addr),
Message::SetIpfixBindPort(port) => self.set_ipfix_bind_port(&port),
Message::SetIpfixAddr(addr) => self.set_ipfix_addr(addr),
Message::SetIpfixPort(port) => self.set_ipfix_port(port),
Message::PendingHosts(cap_id, host_msgs) => self.pending_hosts(cap_id, host_msgs),
Message::OfflineGap(cap_id, gap) => self.offline_gap(cap_id, gap),
Message::Periodic => self.periodic(),
@ -507,8 +506,7 @@ fn set_capture_source(&mut self, cs_pick: CaptureSourcePicklist) {
self.set_pcap_import(self.conf.import_pcap_path.clone());
}
CaptureSourcePicklist::Ipfix => {
self.capture_source =
CaptureSource::Ipfix(MyIpfixCollector::from_conf(&self.conf.ipfix_collector));
self.capture_source = CaptureSource::Ipfix(self.conf.ipfix_socket.clone());
}
CaptureSourcePicklist::Device => {
self.device_selection(&self.conf.device.device_name.clone());
@ -820,24 +818,14 @@ fn set_pcap_import(&mut self, path: String) {
}
}
fn set_ipfix_bind_addr(&mut self, addr: String) {
self.conf.ipfix_collector.bind_addr = addr;
self.capture_source =
CaptureSource::Ipfix(MyIpfixCollector::from_conf(&self.conf.ipfix_collector));
fn set_ipfix_addr(&mut self, addr: String) {
self.conf.ipfix_socket.set_addr(addr);
self.capture_source = CaptureSource::Ipfix(self.conf.ipfix_socket.clone());
}
fn set_ipfix_bind_port(&mut self, raw: &str) {
// Accept anything that parses as u16; ignore other input so the user can
// edit the field freely without losing focus.
if let Ok(port) = raw.parse::<u16>() {
self.conf.ipfix_collector.bind_port = port;
self.capture_source =
CaptureSource::Ipfix(MyIpfixCollector::from_conf(&self.conf.ipfix_collector));
} else if raw.is_empty() {
self.conf.ipfix_collector.bind_port = 0;
self.capture_source =
CaptureSource::Ipfix(MyIpfixCollector::from_conf(&self.conf.ipfix_collector));
}
fn set_ipfix_port(&mut self, port: String) {
self.conf.ipfix_socket.set_port(port);
self.capture_source = CaptureSource::Ipfix(self.conf.ipfix_socket.clone());
}
fn pending_hosts(&mut self, cap_id: usize, host_msgs: Vec<HostMessage>) {
@ -1000,9 +988,6 @@ fn start(&mut self) -> Task<Message> {
let current_device_name = &self.capture_source.get_name();
self.device_selection(current_device_name);
}
if matches!(&self.capture_source, CaptureSource::Ipfix(_)) {
return self.start_ipfix();
}
let pcap_path = self.conf.export_pcap.full_path();
let capture_context =
CaptureContext::new(&self.capture_source, pcap_path.as_ref(), &self.conf.filters);
@ -1018,27 +1003,46 @@ fn start(&mut self) -> Task<Message> {
.set_link_type(capture_context.my_link_type());
self.capture_source.set_addresses();
let capture_source = self.capture_source.clone();
self.traffic_chart
.change_capture_source(matches!(capture_source, CaptureSource::Device(_)));
self.traffic_chart.change_capture_source(matches!(
capture_source,
CaptureSource::Device(_) | CaptureSource::Ipfix(_)
));
let (tx, rx) = async_channel::unbounded();
let (freeze_tx, freeze_rx) = tokio::sync::broadcast::channel(1_048_575);
let freeze_rx2 = freeze_tx.subscribe();
let filters = self.conf.filters.clone();
let _ = thread::Builder::new()
.name("thread_parse_packets".to_string())
.spawn(move || {
parse_packets(
curr_cap_id,
capture_source,
mmdb_readers,
&ip_blacklist,
capture_context,
filters,
&tx,
(freeze_rx, freeze_rx2),
);
})
.log_err(location!());
if let CaptureSource::Ipfix(collector) = &self.capture_source {
let collector = collector.clone();
let _ = thread::Builder::new()
.name("thread_collect_ipfix".to_string())
.spawn(move || {
collect_ipfix(
curr_cap_id,
capture_context,
mmdb_readers,
&ip_blacklist,
&tx,
(freeze_rx, freeze_rx2),
);
})
.log_err(location!());
} else {
let _ = thread::Builder::new()
.name("thread_parse_packets".to_string())
.spawn(move || {
parse_packets(
curr_cap_id,
capture_source,
mmdb_readers,
&ip_blacklist,
capture_context,
filters,
&tx,
(freeze_rx, freeze_rx2),
);
})
.log_err(location!());
}
self.current_capture_rx.1 = Some(rx.clone());
self.freeze_tx = Some(freeze_tx);
@ -1085,47 +1089,6 @@ fn start(&mut self) -> Task<Message> {
Task::none()
}
fn start_ipfix(&mut self) -> Task<Message> {
let CaptureSource::Ipfix(collector) = self.capture_source.clone() else {
return Task::none();
};
self.pcap_error = None;
self.running_page = Some(self.conf.last_opened_page);
let curr_cap_id = self.current_capture_rx.0;
let mmdb_readers = self.mmdb_readers.clone();
let ip_blacklist = self.ip_blacklist.clone();
self.traffic_chart.change_capture_source(true);
let (tx, rx) = async_channel::unbounded();
let (freeze_tx, freeze_rx) = tokio::sync::broadcast::channel(1_048_575);
let freeze_rx2 = freeze_tx.subscribe();
let _ = thread::Builder::new()
.name("thread_collect_ipfix".to_string())
.spawn(move || {
collect_ipfix(
curr_cap_id,
&collector,
mmdb_readers,
&ip_blacklist,
&tx,
(freeze_rx, freeze_rx2),
);
})
.log_err(location!());
self.current_capture_rx.1 = Some(rx.clone());
self.freeze_tx = Some(freeze_tx);
Task::run(rx, |backend_msg| match backend_msg {
BackendTrafficMessage::TickRun(cap_id, msg, host_msg, no_more_packets) => {
Message::TickRun(cap_id, msg, host_msg, no_more_packets)
}
BackendTrafficMessage::PendingHosts(cap_id, host_msg) => {
Message::PendingHosts(cap_id, host_msg)
}
BackendTrafficMessage::OfflineGap(cap_id, gap) => Message::OfflineGap(cap_id, gap),
})
}
fn reset(&mut self) -> Task<Message> {
// close capture channel to kill previous captures
if let Some(rx) = &self.current_capture_rx.1 {
@ -1480,8 +1443,8 @@ fn register_sigint_handler() -> Task<Message> {
pub fn is_capture_source_consistent(&self) -> bool {
match (self.conf.capture_source_picklist, &self.capture_source) {
(CaptureSourcePicklist::Device, CaptureSource::Device(_))
| (CaptureSourcePicklist::File, CaptureSource::File(_)) => true,
(CaptureSourcePicklist::Ipfix, CaptureSource::Ipfix(c)) => c.is_valid(),
| (CaptureSourcePicklist::File, CaptureSource::File(_))
| (CaptureSourcePicklist::Ipfix, CaptureSource::Ipfix(_)) => true,
_ => false,
}
}
@ -2278,7 +2241,7 @@ fn test_conf() {
directory: "/".to_string()
},
import_pcap_path: "/test.pcap".to_string(),
ipfix_collector: Default::default(),
ipfix_socket: Default::default(),
data_repr: DataRepr::Bits,
}
);

View File

@ -5,7 +5,7 @@
use crate::gui::types::favorite::Favorites;
use crate::gui::types::filters::Filters;
use crate::gui::types::settings::Settings;
use crate::networking::ipfix::IpfixCollectorConf;
use crate::networking::ipfix::MyIpfixSocket;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::networking::types::config_device::ConfigDevice;
use crate::networking::types::data_representation::DataRepr;
@ -48,9 +48,6 @@ pub struct Conf {
/// Import path for PCAP file
#[serde(deserialize_with = "deserialize_or_default")]
pub import_pcap_path: String,
/// IPFIX collector configuration (bind address and port)
#[serde(deserialize_with = "deserialize_or_default")]
pub ipfix_collector: IpfixCollectorConf,
/// Remembers the last opened setting page
#[serde(deserialize_with = "deserialize_or_default")]
pub last_opened_setting: SettingsPage,
@ -94,6 +91,9 @@ pub struct Conf {
/// Information about PCAP file export
#[serde(deserialize_with = "deserialize_or_default")]
pub export_pcap: ExportPcap,
/// IPFIX collector configuration (bind address and port)
#[serde(deserialize_with = "deserialize_or_default")]
pub ipfix_socket: MyIpfixSocket,
/// Parameters from settings pages
#[serde(deserialize_with = "deserialize_or_default")]
pub settings: Settings,

View File

@ -146,9 +146,9 @@ pub enum Message {
/// Set the pcap import path
SetPcapImport(String),
/// Set the IPFIX collector bind address
SetIpfixBindAddr(String),
SetIpfixAddr(String),
/// Set the IPFIX collector bind port
SetIpfixBindPort(String),
SetIpfixPort(String),
/// Sent by the backend parsing packets at the end of an offline capture; includes all the pending hosts
PendingHosts(usize, Vec<HostMessage>),
/// Sent by offline captures: ticks without packets

View File

@ -11,7 +11,7 @@
use crate::location;
use crate::mmdb::types::mmdb_reader::MmdbReaders;
use crate::networking::ipfix::MyIpfixCollector;
use crate::networking::ipfix::MyIpfixSocket;
use crate::networking::ipfix::templates::TemplateCache;
use crate::networking::ipfix::wire::{
self, FlowRecord, IPFIX_VERSION, Set, decode_data_record, format_mac, parse_message,
@ -25,6 +25,7 @@
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::arp_type::ArpType;
use crate::networking::types::bogon::is_bogon;
use crate::networking::types::capture_context::{CaptureContext, CaptureType};
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::icmp_type::IcmpType;
@ -47,7 +48,7 @@
/// second with the accumulated `InfoTraffic`.
pub fn collect_ipfix(
cap_id: usize,
collector: &MyIpfixCollector,
capture_context: CaptureContext,
mmdb_readers: MmdbReaders,
ip_blacklist: &IpBlacklist,
tx: &Sender<BackendTrafficMessage>,
@ -55,19 +56,9 @@ pub fn collect_ipfix(
) {
let (mut freeze_rx, _freeze_rx_2) = freeze_rxs;
let Some(bind) = collector.socket_addr() else {
let (Some(CaptureType::Ipfix(socket)), None) = capture_context.consume() else {
return;
};
let Ok(socket) = UdpSocket::bind(bind).log_err(location!()) else {
return;
};
if socket
.set_read_timeout(Some(RECV_TIMEOUT))
.log_err(location!())
.is_err()
{
return;
}
let mut info_traffic_msg = InfoTraffic::default();
let mut templates = TemplateCache::new();
@ -416,6 +407,10 @@ fn build_key(record: &FlowRecord) -> Option<AddressPortPair> {
/// Build a `[Address]` slice carrying just the exporter's IP, so direction
/// classification treats the exporter as the local anchor.
fn exporter_as_addresses(peer: IpAddr) -> Vec<Address> {
if peer.is_loopback() || peer.is_unspecified() {
return vec![];
}
vec![Address {
addr: peer,
netmask: None,

View File

@ -9,8 +9,11 @@
pub mod templates;
pub mod wire;
use crate::location;
use crate::utils::error_logger::ErrorLogger;
use serde::{Deserialize, Serialize};
use std::net::{IpAddr, SocketAddr};
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::time::Duration;
/// IANA-registered default IPFIX collector port.
pub const DEFAULT_IPFIX_PORT: u16 = 4739;
@ -18,52 +21,50 @@
/// Persisted IPFIX collector configuration.
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct IpfixCollectorConf {
pub bind_addr: String,
pub bind_port: u16,
pub struct MyIpfixSocket {
addr: String,
port: String,
}
impl Default for IpfixCollectorConf {
fn default() -> Self {
Self {
bind_addr: String::from("0.0.0.0"),
bind_port: DEFAULT_IPFIX_PORT,
}
}
}
/// Runtime handle for an IPFIX collector source, embedded in `CaptureSource::Ipfix`.
#[derive(Clone, Debug)]
pub struct MyIpfixCollector {
bind_addr: String,
bind_port: u16,
}
impl MyIpfixCollector {
pub fn new(bind_addr: String, bind_port: u16) -> Self {
Self {
bind_addr,
bind_port,
}
impl MyIpfixSocket {
pub fn addr(&self) -> &str {
&self.addr
}
pub fn from_conf(conf: &IpfixCollectorConf) -> Self {
Self::new(conf.bind_addr.clone(), conf.bind_port)
pub fn port(&self) -> &str {
&self.port
}
/// Parse the configured bind address + port into a `SocketAddr`. Returns
/// `None` if `bind_addr` is not a valid IP literal.
pub fn socket_addr(&self) -> Option<SocketAddr> {
let ip: IpAddr = self.bind_addr.parse().ok()?;
Some(SocketAddr::new(ip, self.bind_port))
pub fn set_addr(&mut self, addr: String) {
self.addr = addr;
}
/// Whether the current config can be used to start a capture.
pub fn is_valid(&self) -> bool {
self.socket_addr().is_some()
pub fn set_port(&mut self, port: String) {
self.port = port;
}
pub fn display_name(&self) -> String {
format!("{}:{}", self.bind_addr, self.bind_port)
format!("{}:{}", self.addr, self.port)
}
pub fn socket_addr(&self) -> Result<SocketAddr, String> {
let port = self
.port
.parse::<u16>()
.map_err(|_| format!("Invalid port number: {}", self.port))?;
let ip_addr = self
.addr
.parse::<IpAddr>()
.map_err(|_| format!("Invalid IP address: {}", self.addr))?;
Ok(SocketAddr::new(ip_addr, port))
}
}
impl Default for MyIpfixSocket {
fn default() -> Self {
Self {
addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED).to_string(),
port: DEFAULT_IPFIX_PORT.to_string(),
}
}
}

View File

@ -309,8 +309,6 @@ pub(super) fn get_sniffable_headers(
MyLinkType::LinuxSll(_) => from_linux_sll(packet, true),
MyLinkType::LinuxSll2(_) => from_linux_sll(packet, false),
MyLinkType::Null(_) | MyLinkType::Loop(_) => from_null(packet),
// IPFIX never flows through this pcap-based path.
MyLinkType::Ipfix => None,
}
}

View File

@ -1,7 +1,7 @@
use crate::gui::types::conf::Conf;
use crate::gui::types::filters::Filters;
use crate::location;
use crate::networking::ipfix::MyIpfixCollector;
use crate::networking::ipfix::MyIpfixSocket;
use crate::networking::types::my_device::MyDevice;
use crate::networking::types::my_link_type::MyLinkType;
use crate::translations::translations::network_adapter_translation;
@ -11,11 +11,14 @@
use crate::utils::error_logger::{ErrorLogger, Location};
use pcap::{Active, Address, Capture, Device, Error, Packet, Savefile, Stat};
use serde::{Deserialize, Serialize};
use std::net::UdpSocket;
use std::time::Duration;
pub enum CaptureContext {
Live(Live),
LiveWithSavefile(LiveWithSavefile),
Offline(Offline),
Ipfix(Ipfix),
Error(String),
}
@ -23,7 +26,7 @@ impl CaptureContext {
pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>, filters: &Filters) -> Self {
let mut cap_type = match CaptureType::from_source(source, pcap_out_path) {
Ok(c) => c,
Err(e) => return Self::Error(e.to_string()),
Err(e) => return Self::Error(e),
};
// only apply BPF filter if it is active, and return an error if it fails to apply
@ -36,6 +39,7 @@ pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>, filters: &Fil
let cap = match cap_type {
CaptureType::Live(cap) => cap,
CaptureType::Offline(cap) => return Self::new_offline(cap),
CaptureType::Ipfix(socket) => return Self::Ipfix(Ipfix { socket }),
};
if let Some(out_path) = pcap_out_path {
@ -78,6 +82,7 @@ pub fn consume(self) -> (Option<CaptureType>, Option<Savefile>) {
(Some(CaptureType::Live(onws.live.cap)), Some(onws.savefile))
}
Self::Offline(off) => (Some(CaptureType::Offline(off.cap)), None),
Self::Ipfix(ipfix) => (Some(CaptureType::Ipfix(ipfix.socket)), None),
Self::Error(_) => (None, None),
}
}
@ -89,6 +94,7 @@ pub fn my_link_type(&self) -> MyLinkType {
MyLinkType::from_pcap_link_type(onws.live.cap.get_datalink())
}
Self::Offline(off) => MyLinkType::from_pcap_link_type(off.cap.get_datalink()),
Self::Ipfix(_) => MyLinkType::default(),
Self::Error(_) => MyLinkType::default(),
}
}
@ -107,9 +113,14 @@ pub struct Offline {
cap: Capture<pcap::Offline>,
}
pub struct Ipfix {
socket: UdpSocket,
}
pub enum CaptureType {
Live(Capture<Active>),
Offline(Capture<pcap::Offline>),
Ipfix(UdpSocket),
}
impl CaptureType {
@ -117,6 +128,9 @@ pub fn next_packet(&mut self) -> Result<Packet<'_>, Error> {
match self {
Self::Live(on) => on.next_packet(),
Self::Offline(off) => off.next_packet(),
Self::Ipfix(_) => Err(Error::PcapError(
"Cannot capture packets from IPFIX source".into(),
)),
}
}
@ -124,13 +138,17 @@ pub fn stats(&mut self) -> Result<Stat, Error> {
match self {
Self::Live(on) => on.stats(),
Self::Offline(off) => off.stats(),
Self::Ipfix(_) => Err(Error::PcapError(
"Cannot get stats from IPFIX source".into(),
)),
}
}
fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result<Self, Error> {
fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result<Self, String> {
match source {
CaptureSource::Device(device) => {
let inactive = Capture::from_device(device.to_pcap_device())?;
let inactive =
Capture::from_device(device.to_pcap_device()).map_err(|e| e.to_string())?;
let cap = inactive
.promisc(false)
.buffer_size(2_000_000) // 2MB buffer -> 10k packets of 200 bytes
@ -141,13 +159,21 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result
})
.immediate_mode(false)
.timeout(150) // ensure UI is updated even if no packets are captured
.open()?;
.open()
.map_err(|e| e.to_string())?;
Ok(Self::Live(cap))
}
CaptureSource::File(file) => Ok(Self::Offline(Capture::from_file(&file.path)?)),
CaptureSource::Ipfix(_) => Err(Error::PcapError(String::from(
"IPFIX collector does not use a pcap capture",
))),
CaptureSource::File(file) => Ok(Self::Offline(
Capture::from_file(&file.path).map_err(|e| e.to_string())?,
)),
CaptureSource::Ipfix(ipfix_socket) => {
let socket = UdpSocket::bind(ipfix_socket.socket_addr()?)
.map_err(|e| format!("Failed to bind UDP socket: {e}"))?;
socket
.set_read_timeout(Some(Duration::from_millis(150)))
.map_err(|e| format!("Failed to set socket read timeout: {e}"))?;
Ok(Self::Ipfix(socket))
}
}
}
@ -155,6 +181,9 @@ fn set_bpf(&mut self, bpf: &str) -> Result<(), Error> {
match self {
Self::Live(cap) => cap.filter(bpf, true),
Self::Offline(cap) => cap.filter(bpf, true),
Self::Ipfix(_) => Err(Error::PcapError(
"Cannot set BPF filter on IPFIX source".into(),
)),
}
}
@ -179,7 +208,7 @@ pub fn resume(&mut self, filters: &Filters) {
pub enum CaptureSource {
Device(MyDevice),
File(MyPcapImport),
Ipfix(MyIpfixCollector),
Ipfix(MyIpfixSocket),
}
impl CaptureSource {
@ -193,9 +222,7 @@ pub fn from_conf(conf: &Conf) -> Self {
let path = conf.import_pcap_path.clone();
Self::File(MyPcapImport::new(path))
}
CaptureSourcePicklist::Ipfix => {
Self::Ipfix(MyIpfixCollector::from_conf(&conf.ipfix_collector))
}
CaptureSourcePicklist::Ipfix => Self::Ipfix(conf.ipfix_socket.clone()),
}
}
@ -238,7 +265,7 @@ pub fn get_link_type(&self) -> MyLinkType {
match self {
Self::Device(device) => device.get_link_type(),
Self::File(file) => file.link_type,
Self::Ipfix(_) => MyLinkType::Ipfix,
Self::Ipfix(_) => MyLinkType::default(),
}
}

View File

@ -15,8 +15,6 @@ pub enum MyLinkType {
LinuxSll(Linktype),
LinuxSll2(Linktype),
Unsupported(Linktype),
/// IPFIX collector — flow records over UDP, no underlying link layer
Ipfix,
#[default]
NotYetAssigned,
}
@ -62,9 +60,6 @@ pub fn full_print_on_one_line(self, language: Language) -> String {
}
)
}
Self::Ipfix => {
format!("{}: IPFIX", link_type_translation(language))
}
Self::NotYetAssigned => {
format!("{}: -", link_type_translation(language))
}