diff --git a/.config/config.json5 b/.config/config.json5 index d1fdf11..8143260 100644 --- a/.config/config.json5 +++ b/.config/config.json5 @@ -8,6 +8,7 @@ "": "InputMode", "": "Graph", "

": "Packets", + "": "Dump", "": "Interface", "": "Scan", "": "Up", diff --git a/src/action.rs b/src/action.rs index 3928a90..8b1b45d 100644 --- a/src/action.rs +++ b/src/action.rs @@ -33,6 +33,7 @@ pub enum Action { Right, GraphToggle, PacketToggle, + DumpToggle, InterfaceSwitch, ScanCidr, ActiveInterface(NetworkInterface), @@ -69,6 +70,7 @@ impl<'de> Deserialize<'de> for Action { "NormalMode" => Ok(Action::ModeChange(Mode::Normal)), "Graph" => Ok(Action::GraphToggle), "Packets" => Ok(Action::PacketToggle), + "Dump" => Ok(Action::DumpToggle), "Interface" => Ok(Action::InterfaceSwitch), "Scan" => Ok(Action::ScanCidr), "Up" => Ok(Action::Up), diff --git a/src/components/interfaces.rs b/src/components/interfaces.rs index 2853bfa..163edc7 100644 --- a/src/components/interfaces.rs +++ b/src/components/interfaces.rs @@ -45,7 +45,8 @@ impl Interfaces { if intf.is_up() && !intf.ips.is_empty() { for ip in &intf.ips { // -- set active interface that's not localhost - if ip.is_ipv4() && ip.ip().to_string() != "127.0.0.1" { + // if ip.is_ipv4() && ip.ip().to_string() != "127.0.0.1" { + if ip.ip().to_string() != "127.0.0.1" { self.active_interfaces.push(intf.clone()); break; } diff --git a/src/components/packetdump.rs b/src/components/packetdump.rs index 8481882..ad6acdf 100644 --- a/src/components/packetdump.rs +++ b/src/components/packetdump.rs @@ -5,11 +5,13 @@ use crossterm::event::{KeyCode, KeyEvent}; use crossterm::style::Stylize; use ipnetwork::Ipv4Network; use pnet::datalink::{Channel, NetworkInterface}; +use pnet::packet::icmpv6::{Icmpv6Type, Icmpv6Types}; use pnet::packet::PrimitiveValues; use pnet::packet::{ arp::{ArpHardwareTypes, ArpOperations, ArpPacket, MutableArpPacket}, ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket}, icmp::{echo_reply, echo_request, IcmpPacket, IcmpTypes}, + icmpv6::Icmpv6Packet, ip::{IpNextHeaderProtocol, IpNextHeaderProtocols}, ipv4::Ipv4Packet, tcp::TcpPacket, @@ -34,8 +36,8 @@ use crate::{ action::Action, config::{Config, KeyBindings}, enums::{ - ARPPacketInfo, ICMPPacketInfo, PacketTypeEnum, PacketsInfoTypesEnum, TCPPacketInfo, - UDPPacketInfo, + ARPPacketInfo, ICMP6PacketInfo, ICMPPacketInfo, PacketTypeEnum, PacketsInfoTypesEnum, + TCPPacketInfo, UDPPacketInfo, }, utils::MaxSizeVec, }; @@ -58,10 +60,12 @@ pub struct PacketDump { table_state: TableState, scrollbar_state: ScrollbarState, packet_type: PacketTypeEnum, + arp_packets: MaxSizeVec<(DateTime, PacketsInfoTypesEnum)>, udp_packets: MaxSizeVec<(DateTime, PacketsInfoTypesEnum)>, tcp_packets: MaxSizeVec<(DateTime, PacketsInfoTypesEnum)>, icmp_packets: MaxSizeVec<(DateTime, PacketsInfoTypesEnum)>, + icmp6_packets: MaxSizeVec<(DateTime, PacketsInfoTypesEnum)>, all_packets: MaxSizeVec<(DateTime, PacketsInfoTypesEnum)>, } @@ -86,6 +90,7 @@ impl PacketDump { udp_packets: MaxSizeVec::new(1000), tcp_packets: MaxSizeVec::new(1000), icmp_packets: MaxSizeVec::new(1000), + icmp6_packets: MaxSizeVec::new(1000), all_packets: MaxSizeVec::new(1000), } } @@ -175,6 +180,36 @@ impl PacketDump { } } + fn handle_icmpv6_packet( + interface_name: &str, + source: IpAddr, + destination: IpAddr, + packet: &[u8], + tx: UnboundedSender, + ) { + let icmpv6_packet = Icmpv6Packet::new(packet); + if let Some(icmpv6_packet) = icmpv6_packet { + tx.send(Action::PacketDump( + Local::now(), + PacketsInfoTypesEnum::Icmp6(ICMP6PacketInfo { + interface_name: interface_name.to_string(), + source, + destination, + icmp_type: icmpv6_packet.get_icmpv6_type(), + }), + PacketTypeEnum::Icmp6, + )) + .unwrap(); + // println!( + // "[{}]: ICMPv6 packet {} -> {} (type={:?})", + // interface_name, + // source, + // destination, + // icmpv6_packet.get_icmpv6_type() + // ) + } + } + fn handle_tcp_packet( interface_name: &str, source: IpAddr, @@ -218,6 +253,9 @@ impl PacketDump { IpNextHeaderProtocols::Icmp => { Self::handle_icmp_packet(interface_name, source, destination, packet, tx) } + IpNextHeaderProtocols::Icmpv6 => { + Self::handle_icmpv6_packet(interface_name, source, destination, packet, tx) + } _ => {} // _ => println!( // "[{}]: Unknown {} packet: {} > {}; protocol: {:?} length: {}", // interface_name, @@ -347,6 +385,17 @@ impl PacketDump { tx.clone(), ); continue; + } else if version == 6 { + fake_ethernet_frame.set_destination(MacAddr(0, 0, 0, 0, 0, 0)); + fake_ethernet_frame.set_source(MacAddr(0, 0, 0, 0, 0, 0)); + fake_ethernet_frame.set_ethertype(EtherTypes::Ipv6); + fake_ethernet_frame.set_payload(&packet[payload_offset..]); + Self::handle_ethernet_frame( + &interface, + &fake_ethernet_frame.to_immutable(), + tx.clone(), + ); + continue; } } } @@ -381,6 +430,7 @@ impl PacketDump { PacketTypeEnum::Tcp => self.tcp_packets.get_vec(), PacketTypeEnum::Udp => self.udp_packets.get_vec(), PacketTypeEnum::Icmp => self.icmp_packets.get_vec(), + PacketTypeEnum::Icmp6 => self.icmp6_packets.get_vec(), PacketTypeEnum::All => self.all_packets.get_vec(), } } @@ -486,6 +536,54 @@ impl PacketDump { spans.push(Span::styled(")", Style::default().fg(Color::Yellow))); } // ----------------------------- + // -- ICMP6 + PacketsInfoTypesEnum::Icmp6(icmp) => { + spans.push(Span::styled( + format!("[{}] ", icmp.interface_name.clone()), + Style::default().fg(Color::Green), + )); + spans.push(Span::styled( + "ICMP6", + Style::default().fg(Color::Red).bg(Color::Black), + )); + + let mut icmp_type_str = "unknown"; + match icmp.icmp_type { + Icmpv6Types::EchoRequest => { + icmp_type_str = " echo request"; + } + Icmpv6Types::EchoReply => { + icmp_type_str = " echo reply"; + } + Icmpv6Types::NeighborAdvert => { + icmp_type_str = " neighbor advert"; + } + Icmpv6Types::NeighborSolicit => { + icmp_type_str = " neighbor solicit"; + } + Icmpv6Types::Redirect => { + icmp_type_str = " redirect"; + } + _ => {} + } + spans.push(Span::styled( + icmp_type_str, + Style::default().fg(Color::Yellow), + )); + + spans.push(Span::styled( + format!("{}", icmp.source.to_string()), + Style::default().fg(Color::Blue), + )); + spans.push(Span::styled(" -> ", Style::default().fg(Color::Yellow))); + spans.push(Span::styled( + format!("{}", icmp.destination.to_string()), + Style::default().fg(Color::Blue), + )); + spans.push(Span::styled(", ", Style::default().fg(Color::Yellow))); + spans.push(Span::styled(")", Style::default().fg(Color::Yellow))); + } + // ----------------------------- // -- UDP PacketsInfoTypesEnum::Udp(udp) => { spans.push(Span::styled( diff --git a/src/enums.rs b/src/enums.rs index b9edbdc..3f6b5f1 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -3,7 +3,7 @@ use std::net::{IpAddr, Ipv4Addr}; use pnet::{ packet::{ arp::{ArpOperation, ArpOperations}, - icmp::IcmpType, + icmp::IcmpType, icmpv6::Icmpv6Type, }, util::MacAddr, }; @@ -49,12 +49,21 @@ pub struct ICMPPacketInfo { pub icmp_type: IcmpType, } +#[derive(Debug, Clone, PartialEq)] +pub struct ICMP6PacketInfo { + pub interface_name: String, + pub source: IpAddr, + pub destination: IpAddr, + pub icmp_type: Icmpv6Type, +} + #[derive(Debug, Clone, PartialEq)] pub enum PacketsInfoTypesEnum { Arp(ARPPacketInfo), Tcp(TCPPacketInfo), Udp(UDPPacketInfo), Icmp(ICMPPacketInfo), + Icmp6(ICMP6PacketInfo), } #[derive(Default, Clone, Copy, Display, FromRepr, EnumIter, EnumCount, PartialEq, Debug)] @@ -70,6 +79,8 @@ pub enum PacketTypeEnum { Udp, #[strum(to_string = "ICMP")] Icmp, + #[strum(to_string = "ICMP6")] + Icmp6, } impl PacketTypeEnum { diff --git a/src/network.rs b/src/network.rs deleted file mode 100644 index eedbc06..0000000 --- a/src/network.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::action::Action; -use std::sync::Arc; -use std::thread::{self, JoinHandle}; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; - -pub struct Network { - action_tx: UnboundedSender, - action_rx: UnboundedReceiver, - t_handle: Option>, -} - -impl Network { - fn new(action_tx: UnboundedSender, action_rx: UnboundedReceiver) -> Self { - let t_handle = thread::spawn(move || { - Self::thread_net(action_tx.clone(), &action_rx); - }); - - Self { - action_tx, - action_rx, - t_handle: Some(t_handle), - } - } - - fn thread_net(action_tx: UnboundedSender, ref action_rx: UnboundedReceiver) { - loop { - std::thread::sleep(std::time::Duration::from_millis(1)); - } - } -}