Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ ipnet = "2.12.0"
jiff = "0.2.35"
listeners = "0.6.0"
maxminddb = "0.30.0"
nom = "8.0.0"
pcap = "2.4.0"
phf = "0.14.0"
phf_shared = "0.14.0"
Expand Down
1 change: 1 addition & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ mod tests {
expanded: true,
},
import_pcap_path: "whole_day.pcapng".to_string(),
ipfix_socket: Default::default(),
export_pcap: ExportPcap {
enabled: true,
file_name: "sniffnet.pcap".to_string(),
Expand Down
18 changes: 12 additions & 6 deletions src/gui/pages/connection_details_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,17 @@ fn col_info<'a>(
));
}

if is_icmp || is_arp {
// Empty for ICMP flows learned from IPFIX, whose records don't carry the
// message type unless the exporter sends the relevant IEs.
let messages = if is_icmp {
IcmpType::pretty_print_types(&val.icmp_types)
} else if is_arp {
ArpType::pretty_print_types(&val.arp_types)
} else {
String::new()
};

if !messages.is_empty() {
ret_val = ret_val.push(
Column::new()
.push(
Expand All @@ -252,11 +262,7 @@ fn col_info<'a>(
.push(Scrollable::with_direction(
Column::new()
.padding(Padding::ZERO.right(10).bottom(10))
.push(Text::new(if is_icmp {
IcmpType::pretty_print_types(&val.icmp_types)
} else {
ArpType::pretty_print_types(&val.arp_types)
})),
.push(Text::new(messages)),
Direction::Both {
vertical: ScrollbarType::properties(),
horizontal: ScrollbarType::properties(),
Expand Down
87 changes: 72 additions & 15 deletions src/gui/pages/initial_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ use crate::gui::types::export_pcap::ExportPcap;
use crate::gui::types::filters::Filters;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::ipfix::{DEFAULT_IPFIX_ADDR, DEFAULT_IPFIX_PORT, MyIpfixSocket};
use crate::networking::types::capture_context::{CaptureSource, CaptureSourcePicklist};
use crate::networking::types::my_device::MyDevice;
use crate::networking::types::my_link_type::MyLinkType;
use crate::translations::translations::{network_adapter_translation, start_translation};
use crate::translations::translations_3::{
directory_translation, export_capture_translation, file_name_translation,
directory_translation, export_capture_translation, file_name_translation, port_translation,
};
use crate::translations::translations_4::capture_file_translation;
use crate::translations::translations_5::{filter_traffic_translation, traffic_source_translation};
use crate::translations::translations_6::{bind_address_translation, ipfix_collector_translation};
use crate::utils::formatted_strings::get_path_termination_string;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
Expand All @@ -46,14 +48,18 @@ pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {

let col_data_source = get_col_data_source(sniffer, language);

let col_checkboxes = Column::new()
.spacing(10)
.push(get_filters_group(&sniffer.conf.filters, language))
.push(get_export_pcap_group_maybe(
sniffer.conf.capture_source_picklist,
&sniffer.conf.export_pcap,
language,
));
let mut col_checkboxes = Column::new().spacing(10);
if !matches!(
sniffer.conf.capture_source_picklist,
CaptureSourcePicklist::Ipfix
) {
col_checkboxes = col_checkboxes.push(get_filters_group(&sniffer.conf.filters, language));
}
col_checkboxes = col_checkboxes.push(get_export_pcap_group_maybe(
sniffer.conf.capture_source_picklist,
&sniffer.conf.export_pcap,
language,
));

let is_capture_source_consistent = sniffer.is_capture_source_consistent();
let right_col = Column::new()
Expand Down Expand Up @@ -102,22 +108,25 @@ fn button_start<'a>(
}

fn get_col_data_source(sniffer: &Sniffer, language: Language) -> Column<'_, Message, StyleType> {
let current_option = if sniffer.conf.capture_source_picklist == CaptureSourcePicklist::Device {
network_adapter_translation(language)
} else {
capture_file_translation(language)
let current_option = match sniffer.conf.capture_source_picklist {
CaptureSourcePicklist::Device => network_adapter_translation(language),
CaptureSourcePicklist::File => capture_file_translation(language),
CaptureSourcePicklist::Ipfix => ipfix_collector_translation(language),
};
let picklist = PickList::new(
[
network_adapter_translation(language),
capture_file_translation(language),
ipfix_collector_translation(language),
],
Some(current_option),
move |option| {
if option == network_adapter_translation(language) {
Message::SetCaptureSource(CaptureSourcePicklist::Device)
} else {
} else if option == capture_file_translation(language) {
Message::SetCaptureSource(CaptureSourcePicklist::File)
} else {
Message::SetCaptureSource(CaptureSourcePicklist::Ipfix)
}
},
)
Expand Down Expand Up @@ -151,6 +160,12 @@ fn get_col_data_source(sniffer: &Sniffer, language: Language) -> Column<'_, Mess
&sniffer.conf.import_pcap_path,
));
}
CaptureSourcePicklist::Ipfix => {
col = col.push(get_col_ipfix_collector(
language,
&sniffer.conf.ipfix_socket,
));
}
}

col
Expand Down Expand Up @@ -302,6 +317,45 @@ fn get_col_import_pcap<'a>(
Column::new().spacing(5).push(button)
}

fn get_col_ipfix_collector<'a>(
language: Language,
ipfix_socket: &MyIpfixSocket,
) -> Column<'a, Message, StyleType> {
let addr_row = Row::new()
.align_y(Alignment::Center)
.spacing(5)
.push(Text::new(format!(
"{}:",
bind_address_translation(language)
)))
.push(
TextInput::new(&DEFAULT_IPFIX_ADDR.to_string(), &ipfix_socket.addr())
.on_input(Message::SetIpfixAddr)
.padding([2, 5]),
);
let port_row = Row::new()
.align_y(Alignment::Center)
.spacing(5)
.push(Text::new(format!("{}:", port_translation(language))))
.push(
TextInput::new(&DEFAULT_IPFIX_PORT.to_string(), &ipfix_socket.port())
.on_input(Message::SetIpfixPort)
.padding([2, 5]),
);
let content = Column::new()
.width(Length::Fill)
.padding(15)
.spacing(10)
.push(addr_row)
.push(port_row);

Column::new().spacing(5).push(
Container::new(content)
.width(Length::Fill)
.class(ContainerType::BorderedRound),
)
}

fn get_filters_group<'a>(
filters: &Filters,
language: Language,
Expand Down Expand Up @@ -345,7 +399,10 @@ fn get_export_pcap_group_maybe<'a>(
export_pcap: &ExportPcap,
language: Language,
) -> Option<Container<'a, Message, StyleType>> {
if cs_pick == CaptureSourcePicklist::File {
if matches!(
cs_pick,
CaptureSourcePicklist::File | CaptureSourcePicklist::Ipfix
) {
return None;
}

Expand Down
38 changes: 26 additions & 12 deletions src/gui/pages/overview_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,22 +346,36 @@ pub(crate) fn col_device<'a>(
Row::new()
.spacing(10)
.push(Text::new(format!(" {cs_info}")))
.push(get_info_tooltip(
Column::new()
.spacing(10)
.push(Text::new(link_type.full_print_on_one_line(language)))
.push(get_addresses_row(link_type, cs.get_addresses()))
.into(),
)),
.push(
// no tooltip for IPFIX (it has no link type and no addresses)
if matches!(cs, CaptureSource::Ipfix(_)) {
None
} else {
Some(get_info_tooltip(
Column::new()
.spacing(10)
.push(Text::new(link_type.full_print_on_one_line(language)))
.push(get_addresses_row(link_type, cs.get_addresses()))
.into(),
))
},
),
),
)
.push(
Column::new()
.push(
Text::new(format!("{}:", active_filters_translation(language)))
.class(TextType::Subtitle),
// there are no filters for IPFIX
if matches!(cs, CaptureSource::Ipfix(_)) {
None
} else {
Some(
Column::new()
.push(
Text::new(format!("{}:", active_filters_translation(language)))
.class(TextType::Subtitle),
)
.push(Row::new().push(Text::new(" ")).push(filters_desc)),
)
.push(Row::new().push(Text::new(" ")).push(filters_desc)),
},
)
}

Expand Down
39 changes: 34 additions & 5 deletions src/gui/pages/waiting_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ use crate::translations::translations::{
};
use crate::translations::translations_3::unsupported_link_type_translation;
use crate::translations::translations_4::reading_from_pcap_translation;
use crate::translations::translations_6::{
invalid_ipfix_received_translation, make_sure_valid_ipfix_translation,
waiting_ipfix_connections_translation,
};
use crate::utils::types::icon::Icon;
use iced::widget::{Column, Container, Space, Text};
use iced::{Alignment, Length};
Expand All @@ -21,21 +25,46 @@ pub fn waiting_page(sniffer: &Sniffer) -> Option<Container<'_, Message, StyleTyp
let dots = &sniffer.dots_pulse.0;
let cs = &sniffer.capture_source;

let pcap_error = sniffer.pcap_error.as_ref();
let capture_error = sniffer.capture_error.as_ref();
let tot_packets = sniffer
.info_traffic
.tot_data_info
.tot_data(DataRepr::Packets);

if pcap_error.is_none() && tot_packets > 0 {
// a rejected IPFIX exporter doesn't stop the others from being read, so the
// page makes way as soon as any traffic is actually observed
if tot_packets > 0 {
return None;
}

let link_type = cs.get_link_type();
let (icon_text, nothing_to_see_text) = if let Some(error) = pcap_error {
let (icon_text, nothing_to_see_text) = if let Some(error) = capture_error {
// the IPFIX collector reports rejected datagrams as an empty error: the
// capture is running fine, so it's a warning and its text is static.
// A non-empty one is a failure to start, and keeps its own message.
if matches!(cs, CaptureSource::Ipfix(_)) && error.is_empty() {
(
Icon::Warning.to_text().size(60),
format!(
"{}\n\n{}",
invalid_ipfix_received_translation(language),
make_sure_valid_ipfix_translation(language)
),
)
} else {
(
Icon::Error.to_text().size(60),
format!("{}\n\n{error}", error_translation(language)),
)
}
} else if matches!(cs, CaptureSource::Ipfix(_)) {
(
Icon::Error.to_text().size(60),
format!("{}\n\n{error}", error_translation(language)),
Icon::get_hourglass(dots.len()).size(60),
format!(
"{}\n\n{}",
waiting_ipfix_connections_translation(language),
make_sure_valid_ipfix_translation(language)
),
)
} else if !link_type.is_supported() {
(
Expand Down
Loading
Loading