2020-05-13 00:03:45 +02:00
extern crate bitfield ;
2020-05-21 00:54:56 +02:00
extern crate byteorder ;
2020-05-13 00:03:45 +02:00
extern crate eui48 ;
mod packet_handler ;
2020-06-11 18:24:09 +02:00
use pcap ::{ Capture , Linktype } ;
2020-05-21 01:48:13 +02:00
use regex ::bytes ::Regex ;
2020-06-12 23:17:52 +02:00
use std ::convert ::TryInto ;
2020-06-13 03:17:13 +02:00
use std ::str ;
2020-06-04 21:13:48 +02:00
//use std::thread::{spawn, JoinHandle};
//use std::sync::mpsc::{channel, Receiver};
2020-05-21 00:54:56 +02:00
/* protocol ids, LittleEndian */
const ETH_P_IPV6 : usize = 0xDD86 ;
const ETH_P_IP : usize = 0x08 ;
const TCP : usize = 0x06 ;
2020-05-22 19:09:05 +02:00
const UDP : usize = 0x11 ;
2020-06-13 03:17:13 +02:00
const ETH_P_ARP : usize = 0x0608 ;
const ETH_P_RARP : usize = 0x3580 ;
2020-06-14 04:14:06 +02:00
/* Protocol header sizes */
const ETHER_HDRLEN : usize = 0xE ;
const NO_PREDECESSOR : usize = 0x0 ;
const IPV6_HDRLEN : u32 = 0xA ; // I know, this will get changed. It works for now.
2020-06-15 01:08:40 +02:00
/* conditionals */
2020-06-14 04:14:06 +02:00
const IPV4 : usize = 0x4 ;
const IPV6 : usize = 0x6 ;
2020-05-19 19:34:19 +02:00
2020-06-13 03:17:13 +02:00
/*
2020-06-11 01:08:16 +02:00
QryData could be written in the sense of QryData { .. . frame : .. , packet : .. , segment :.. }
2020-06-13 03:17:13 +02:00
On the one hand , only the actual type of frame / packet / segment would be contained in the resulting struct .
2020-06-11 01:08:16 +02:00
So , increased benefit in serialization / cpu time , could result in less data to be serialized , depending on layout .
2020-06-13 03:17:13 +02:00
On the other hand , each datagram ::type needs to implement traits which would need to be dynamically dispatched by returning any of these types per iso level from a single function each . The result would be a performance decrease .
2020-06-12 23:17:52 +02:00
See : https ://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits
2020-06-11 01:08:16 +02:00
See : https ://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch
Then again , parser logic would be fewer lines + more unified using the latter method . Maybe better optimizable as well ? Maybe this is a nice tradeoff ?
2020-06-13 03:17:13 +02:00
TODO : Implement and benchmark dynamically dispatched packet data in conjunction with restructured QryData .
2020-06-11 01:08:16 +02:00
* /
2020-05-13 00:03:45 +02:00
#[ derive(Debug, Clone) ]
2020-05-21 00:54:56 +02:00
pub struct QryData {
pub id : i32 ,
pub time : f64 ,
pub data : Option < Vec < u8 > > ,
2020-06-11 18:24:09 +02:00
pub ether_header : Option < packet_handler ::EtherHeader > ,
2020-05-21 00:54:56 +02:00
pub ipv4_header : Option < packet_handler ::IpV4Header > ,
pub ipv6_header : Option < packet_handler ::IpV6Header > ,
pub tcp_header : Option < packet_handler ::TcpHeader > ,
2020-05-22 19:09:05 +02:00
pub udp_header : Option < packet_handler ::UdpHeader > ,
2020-05-31 00:44:47 +02:00
pub arp_header : Option < packet_handler ::ArpHeader > ,
2020-05-25 00:51:10 +02:00
pub reg_res : Option < String > ,
2020-05-13 00:03:45 +02:00
}
2020-06-12 23:17:52 +02:00
#[ allow(dead_code) ]
2020-06-13 03:17:13 +02:00
enum EncapsulationType {
2020-06-18 01:45:12 +02:00
// pcap::Linktype::get_name() is unsafe. That's why this data structure would be an alternative.
2020-06-13 03:17:13 +02:00
EN10MB = 1 , // See: https://docs.rs/pcap/0.7.0/src/pcap/lib.rs.html#247-261
RAW = 101 , // Would this be an issue?
}
2020-06-07 23:56:53 +02:00
2020-06-11 01:08:16 +02:00
impl QryData {
2020-06-13 03:17:13 +02:00
// This is not cool!
2020-06-18 01:45:12 +02:00
// I don't know if object oriented is the way to go here. It's awful but modular!
2020-06-15 01:08:40 +02:00
// Maybe I'll do a roolback and do a different design
2020-06-13 03:17:13 +02:00
fn new ( ) -> QryData {
QryData {
id : 0 ,
time : 0.0 ,
data : None ,
ether_header : None ::< packet_handler ::EtherHeader > ,
ipv4_header : None ::< packet_handler ::IpV4Header > ,
ipv6_header : None ::< packet_handler ::IpV6Header > ,
tcp_header : None ::< packet_handler ::TcpHeader > ,
udp_header : None ::< packet_handler ::UdpHeader > ,
arp_header : None ::< packet_handler ::ArpHeader > ,
reg_res : None ::< String > ,
}
}
fn encap_en10mb ( & mut self , packet_data : & [ u8 ] ) -> Result < ( ) , core ::fmt ::Error > {
self . ether_header = Some ( packet_handler ::ethernet_handler ( packet_data ) ) . unwrap ( ) ;
match self . ether_header . unwrap ( ) . ether_type as usize {
2020-05-21 01:48:13 +02:00
ETH_P_IP = > {
2020-06-14 04:14:06 +02:00
self . ipv4_header =
Some ( packet_handler ::ip_handler ( packet_data , ETHER_HDRLEN ) ) . unwrap ( ) ;
self . transport_layer (
packet_data ,
self . ipv4_header . unwrap ( ) . ip_protocol as usize ,
self . ipv4_header . unwrap ( ) . ip_ihl ,
ETHER_HDRLEN ,
)
. unwrap ( ) ;
2020-05-25 00:51:10 +02:00
}
2020-05-21 01:48:13 +02:00
ETH_P_IPV6 = > {
2020-06-14 04:14:06 +02:00
self . ipv6_header =
Some ( packet_handler ::ipv6_handler ( packet_data , ETHER_HDRLEN ) ) . unwrap ( ) ;
self . transport_layer (
packet_data ,
self . ipv6_header . unwrap ( ) . next_header as usize ,
IPV6_HDRLEN ,
ETHER_HDRLEN ,
)
. unwrap ( ) ;
2020-05-25 00:51:10 +02:00
}
2020-05-31 00:44:47 +02:00
ETH_P_ARP | ETH_P_RARP = > {
2020-06-14 04:14:06 +02:00
self . arp_header =
Some ( packet_handler ::arp_handler ( packet_data , ETHER_HDRLEN ) ) . unwrap ( ) ;
2020-05-31 00:44:47 +02:00
}
2020-06-11 01:08:16 +02:00
_ = > println! ( " Network protocol not implemented " ) ,
2020-05-21 01:48:13 +02:00
}
2020-06-13 03:17:13 +02:00
Ok ( ( ) )
2020-06-11 01:08:16 +02:00
}
2020-06-13 03:17:13 +02:00
fn encap_raw ( & mut self , packet_data : & [ u8 ] ) -> Result < ( ) , core ::fmt ::Error > {
let ip_version : usize = ( ( packet_data [ 0 ] & 0xf0 ) > > 4 ) . try_into ( ) . unwrap ( ) ;
match ip_version {
2020-06-14 04:14:06 +02:00
IPV4 = > {
self . ipv4_header =
Some ( packet_handler ::ip_handler ( packet_data , NO_PREDECESSOR ) ) . unwrap ( ) ;
self . transport_layer (
packet_data ,
self . ipv4_header . unwrap ( ) . ip_protocol as usize ,
self . ipv4_header . unwrap ( ) . ip_ihl ,
NO_PREDECESSOR ,
)
. unwrap ( ) ;
2020-06-13 03:17:13 +02:00
}
2020-06-14 04:14:06 +02:00
IPV6 = > {
self . ipv6_header =
Some ( packet_handler ::ipv6_handler ( packet_data , NO_PREDECESSOR ) ) . unwrap ( ) ;
self . transport_layer (
packet_data ,
self . ipv6_header . unwrap ( ) . next_header as usize ,
IPV6_HDRLEN ,
NO_PREDECESSOR ,
)
. unwrap ( ) ;
2020-06-13 03:17:13 +02:00
}
_ = > println! ( " Network Protocol not implemented " ) ,
}
Ok ( ( ) )
2020-06-12 23:17:52 +02:00
}
2020-06-18 01:45:12 +02:00
// TODO: impl correct Err type and use as Result
2020-06-13 03:17:13 +02:00
fn transport_layer (
& mut self ,
packet_data : & [ u8 ] ,
protocol_type : usize ,
l3_header_length : u32 ,
ether_hdrlen : usize ,
) -> Result < ( ) , core ::fmt ::Error > {
match protocol_type {
TCP = > {
2020-06-14 04:14:06 +02:00
self . tcp_header = Some ( packet_handler ::tcp_handler (
l3_header_length ,
packet_data ,
ether_hdrlen ,
) )
. unwrap ( ) ;
2020-06-13 03:17:13 +02:00
self . data = Some ( packet_handler ::payload_handler (
l3_header_length ,
self . tcp_header . unwrap ( ) . data_offset ,
packet_data ,
2020-06-14 04:14:06 +02:00
ether_hdrlen ,
2020-06-13 03:17:13 +02:00
) )
. unwrap ( ) ;
}
UDP = > {
2020-06-14 04:14:06 +02:00
self . udp_header = Some ( packet_handler ::udp_handler (
l3_header_length ,
packet_data ,
ether_hdrlen ,
) )
. unwrap ( ) ;
2020-06-13 03:17:13 +02:00
self . data = Some ( packet_handler ::payload_handler (
l3_header_length ,
7 ,
packet_data ,
2020-06-14 04:14:06 +02:00
ether_hdrlen ,
2020-06-13 03:17:13 +02:00
) )
. unwrap ( ) ;
}
_ = > println! ( " Transport layer protocol not implemented " ) ,
}
2020-06-12 23:17:52 +02:00
Ok ( ( ) )
2020-06-11 01:08:16 +02:00
}
}
/* Regex parse _complete_ package */
fn flag_carnage ( re : & Regex , payload : & [ u8 ] ) -> Option < String > {
let mut flags : String = String ::new ( ) ;
for mat in re . find_iter ( payload ) {
// TODO: Test benchmark format! vs. push_str()
// flags.push_str(&format!("{} ",std::str::from_utf8(mat.as_bytes()).unwrap()));
// See: https://github.com/hoodie/concatenation_benchmarks-rs
flags . push_str ( std ::str ::from_utf8 ( mat . as_bytes ( ) ) . unwrap ( ) ) ;
flags . push_str ( " ; " ) ;
}
2020-06-18 01:45:12 +02:00
//if flags.len() > 0{
//println!("{:?}", flags);
//}
2020-06-11 01:08:16 +02:00
match 0 < flags . len ( ) {
false = > None ,
true = > Some ( flags ) ,
}
}
2020-06-12 23:17:52 +02:00
pub fn parse ( parse_file : & std ::path ::Path , filter_str : & str , regex_filter : & str ) -> Vec < QryData > {
2020-06-11 01:08:16 +02:00
let mut v : Vec < QryData > = Vec ::new ( ) ;
let mut cap = Capture ::from_file ( parse_file ) . unwrap ( ) ;
Capture ::filter ( & mut cap , & filter_str ) . unwrap ( ) ;
2020-06-12 23:17:52 +02:00
let linktype = cap . get_datalink ( ) ;
println! ( " {:?} " , & linktype ) ;
2020-06-11 01:08:16 +02:00
let re = Regex ::new ( regex_filter ) . unwrap ( ) ;
while let Ok ( packet ) = cap . next ( ) {
2020-06-13 03:17:13 +02:00
let mut me = QryData ::new ( ) ;
2020-06-12 23:17:52 +02:00
match linktype {
2020-06-15 01:08:40 +02:00
Linktype ( 1 ) = > me . encap_en10mb ( packet . data ) . unwrap ( ) , // I reversed encapsulation/linktype bytes in pcap/pcapng file by looking at https://www.tcpdump.org/linktypes.html
Linktype ( 12 ) = > me . encap_raw ( packet . data ) . unwrap ( ) , // Either this source + my implementation is wrong or pcap crate sucks
2020-06-11 01:08:16 +02:00
_ = > ( ) ,
} ;
2020-06-13 03:17:13 +02:00
2020-06-11 01:08:16 +02:00
me . time = ( packet . header . ts . tv_usec as f64 / 1000000.0 ) + packet . header . ts . tv_sec as f64 ;
2020-06-18 01:45:12 +02:00
me . reg_res = Some ( flag_carnage ( & re , packet . data ) ) . unwrap ( ) ; // Regex overhead is between 4-9% --single threaded-- on complete packet [u8] data
2020-06-13 03:17:13 +02:00
//v.push(me.clone());
2020-06-18 01:45:12 +02:00
if me . reg_res . is_some ( ) {
println! ( " {:?} " , & me . reg_res ) ;
}
2020-06-13 03:17:13 +02:00
v . push ( QryData {
id : 0 ,
time : me . time ,
data : me . data ,
ether_header : me . ether_header ,
ipv4_header : me . ipv4_header ,
ipv6_header : me . ipv6_header ,
tcp_header : me . tcp_header ,
udp_header : me . udp_header ,
arp_header : me . arp_header ,
reg_res : me . reg_res ,
} ) ;
2020-05-13 00:03:45 +02:00
}
v
}
2020-06-04 21:13:48 +02:00
/* This could need some love */
2020-06-13 03:17:13 +02:00
pub fn parse_device (
2020-06-18 01:45:12 +02:00
// Pcap file data parsing will result in less cpu cycles if device parsing is handled in a seperate function, I guess.
// It would result in less conditional overhead?!
2020-06-13 03:17:13 +02:00
parse_device : & str ,
filter_str : & str ,
insert_max : & usize ,
regex_filter : & str ,
) -> Vec < QryData > {
2020-05-13 00:03:45 +02:00
let mut v : Vec < QryData > = Vec ::new ( ) ;
let mut cap = Capture ::from_device ( parse_device ) . unwrap ( ) . open ( ) . unwrap ( ) ;
Capture ::filter ( & mut cap , & filter_str ) . unwrap ( ) ;
2020-06-11 18:24:09 +02:00
let linktype = cap . get_datalink ( ) ;
2020-06-06 03:29:38 +02:00
let re = Regex ::new ( regex_filter ) . unwrap ( ) ;
2020-05-21 00:54:56 +02:00
' parse : while let Ok ( packet ) = cap . next ( ) {
2020-06-13 03:17:13 +02:00
let mut me = QryData ::new ( ) ;
match linktype {
Linktype ( 1 ) = > me . encap_en10mb ( packet . data ) . unwrap ( ) , //me = QryData::encap_en10mb(packet.data).unwrap(),
2020-06-15 01:08:40 +02:00
Linktype ( 12 ) = > me . encap_raw ( packet . data ) . unwrap ( ) , //me = QryData::encap_raw(packet.data).unwrap(),
2020-06-11 18:24:09 +02:00
_ = > ( ) ,
2020-06-13 03:17:13 +02:00
} ;
2020-05-25 00:51:10 +02:00
2020-06-13 03:17:13 +02:00
me . time = ( packet . header . ts . tv_usec as f64 / 1000000.0 ) + packet . header . ts . tv_sec as f64 ;
2020-06-11 18:24:09 +02:00
me . reg_res = flag_carnage ( & re , packet . data ) ;
2020-06-13 03:17:13 +02:00
v . push ( me . clone ( ) ) ;
2020-05-13 00:03:45 +02:00
if & v . len ( ) > = insert_max {
break 'parse ;
}
}
v
}