implemented arp protocol

This commit is contained in:
gurkenhabicht 2020-05-31 00:44:47 +02:00
parent c7355fbcf8
commit ac9cc64fbc
7 changed files with 392 additions and 82 deletions

244
src/\ Normal file
View File

@ -0,0 +1,244 @@
extern crate bitfield;
extern crate byteorder;
extern crate eui48;
mod packet_handler;
use eui48::MacAddress;
use pcap::Capture;
use regex::bytes::Regex;
use std::str;
/* protocol ids, LittleEndian */
const ETH_P_IPV6: usize = 0xDD86;
const ETH_P_IP: usize = 0x08;
const TCP: usize = 0x06;
const UDP: usize = 0x11;
const ETH_P_ARP: usize = 0x0608;
const ETH_P_RARP: usize = 0x3580;
fn build_ether() -> packet_handler::EtherHeader {
packet_handler::EtherHeader {
ether_dhost: (MacAddress::new([0; 6])).to_hex_string(),
ether_shost: (MacAddress::new([0; 6])).to_hex_string(),
ether_type: 0,
}
}
#[derive(Debug, Clone)]
pub struct QryData {
pub id: i32,
pub time: f64,
pub data: Option<Vec<u8>>,
pub ether_header: packet_handler::EtherHeader,
pub ipv4_header: Option<packet_handler::IpV4Header>,
pub ipv6_header: Option<packet_handler::IpV6Header>,
pub tcp_header: Option<packet_handler::TcpHeader>,
pub udp_header: Option<packet_handler::UdpHeader>,
pub arp_header: Option<packet_handler::ArpHeader 'a>,
pub reg_res: Option<String>,
}
fn flag_carnage(re: &Regex, payload: &[u8]) -> Option<String> {
let mut flags: String = String::new();
for mat in re.find_iter(payload) {
flags.push_str(std::str::from_utf8(mat.as_bytes()).unwrap());
}
match 0 < flags.len() {
false => None,
true => Some(flags),
}
}
pub fn parse <'a>(parse_file: &std::path::Path, filter_str: &str) -> Vec<QryData> {
let ether_init = build_ether();
let mut me = QryData {
id: 0,
time: 0.0,
data: None,
ether_header: ether_init,
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<'a>>,
reg_res: None::<String>,
};
let mut v: Vec<QryData> = Vec::new();
let mut cap = Capture::from_file(parse_file).unwrap();
Capture::filter(&mut cap, &filter_str).unwrap();
let re = Regex::new(r"(?:http|https):[[::punct::]]?").unwrap();
while let Ok(packet) = cap.next() {
me.time = (packet.header.ts.tv_usec as f64 / 1000000.0) + packet.header.ts.tv_sec as f64;
me.data = Some(packet.data.to_vec());
me.reg_res = flag_carnage(&re, packet.data);
me.ether_header = packet_handler::ethernet_handler(packet.data);
match me.ether_header.ether_type as usize {
ETH_P_IP => {
me.ipv6_header = None::<packet_handler::IpV6Header>;
me.ipv4_header = Some(packet_handler::ip_handler(packet.data)).unwrap();
match me.ipv4_header.unwrap().ip_protocol as usize {
TCP => {
me.tcp_header = Some(packet_handler::tcp_handler(
me.ipv4_header.unwrap().ip_ihl,
packet.data,
))
.unwrap();
me.data = packet_handler::payload_handler(
me.ipv4_header.unwrap().ip_ihl,
me.tcp_header.unwrap().data_offset,
packet.data,
);
}
UDP => {
me.udp_header = Some(packet_handler::udp_handler(
me.ipv4_header.unwrap().ip_ihl,
packet.data,
))
.unwrap();
me.data = packet_handler::payload_handler(
me.ipv4_header.unwrap().ip_ihl,
7,
packet.data,
);
}
_ => println!("network protocol not implemented"),
}
}
ETH_P_IPV6 => {
me.ipv4_header = None::<packet_handler::IpV4Header>;
me.ipv6_header = Some(packet_handler::ipv6_handler(packet.data)).unwrap();
match me.ipv6_header.unwrap().next_header as usize {
TCP => {
me.tcp_header = Some(packet_handler::tcp_handler(10, packet.data)).unwrap();
me.data = packet_handler::payload_handler(
10,
me.tcp_header.unwrap().data_offset,
packet.data,
);
}
UDP => {
me.udp_header = Some(packet_handler::udp_handler(10, packet.data)).unwrap();
me.data = packet_handler::payload_handler(10, 7, packet.data);
}
_ => println!("network protocol not implemented"),
}
}
ETH_P_ARP | ETH_P_RARP => {
me.arp_header = Some(packet_handler::arp_handler(packet.data)).unwrap();
}
_ => println!("network protocol not implemented"),
}
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,
});
}
v
}
pub fn parse_device(parse_device: &str, filter_str: &str, insert_max: &usize) -> Vec<QryData> {
let ether_init = build_ether();
let mut me = QryData {
id: 0,
time: 0.0,
data: None,
ether_header: ether_init,
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>,
};
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();
let re = Regex::new(r"(?:http|https):[[::punct::]]").unwrap();
'parse: while let Ok(packet) = cap.next() {
me.time = (packet.header.ts.tv_usec as f64 / 1000000.0) + packet.header.ts.tv_sec as f64;
me.data = Some(packet.data.to_vec());
me.reg_res = flag_carnage(&re, packet.data);
me.ether_header = packet_handler::ethernet_handler(packet.data);
match me.ether_header.ether_type as usize {
ETH_P_IP => {
me.ipv6_header = None::<packet_handler::IpV6Header>;
me.ipv4_header = Some(packet_handler::ip_handler(packet.data)).unwrap();
match me.ipv4_header.unwrap().ip_protocol as usize {
TCP => {
me.tcp_header = Some(packet_handler::tcp_handler(
me.ipv4_header.unwrap().ip_ihl,
packet.data,
))
.unwrap();
me.data = packet_handler::payload_handler(
me.ipv4_header.unwrap().ip_ihl,
me.tcp_header.unwrap().data_offset,
packet.data,
);
}
UDP => {
me.udp_header = Some(packet_handler::udp_handler(
me.ipv4_header.unwrap().ip_ihl,
packet.data,
))
.unwrap();
me.data = packet_handler::payload_handler(
me.ipv4_header.unwrap().ip_ihl,
7,
packet.data,
);
}
_ => println!("network protocol not implemented"),
}
}
ETH_P_IPV6 => {
me.ipv4_header = None::<packet_handler::IpV4Header>;
me.ipv6_header = Some(packet_handler::ipv6_handler(packet.data)).unwrap();
match me.ipv6_header.unwrap().next_header as usize {
TCP => {
me.tcp_header = Some(packet_handler::tcp_handler(10, packet.data)).unwrap();
me.data = packet_handler::payload_handler(
10,
me.tcp_header.unwrap().data_offset,
packet.data,
);
}
UDP => {
me.udp_header = Some(packet_handler::udp_handler(10, packet.data)).unwrap();
me.data = packet_handler::payload_handler(10, 7, packet.data);
}
_ => println!("network protocol not implemented"),
}
}
_ => println!("network protocol not implemented"),
}
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,
});
if &v.len() >= insert_max {
break 'parse;
}
}
v
}

View File

@ -1,4 +1,3 @@
// Init of configuration files could also be done via Config crate.
// But at this point of development it seems like unjustified overhead.
@ -15,22 +14,26 @@ pub struct Config {
pub pcap_dir: String,
}
pub fn from_json_file() -> Option<Config> {
let config_file = File::open("parser.json").expect("file should open read only");
let json: serde_json::Value = serde_json::from_reader(config_file).unwrap();
Some(Config {
filter: json.get("filter").unwrap().as_str().unwrap().to_owned(),
insert_max : json.get("insert_max").unwrap().as_u64().unwrap() as usize,
pcap_file : json.get("pcap_file").unwrap().as_str().unwrap().to_owned(),
connection : format!("host={} user={} password={}",
insert_max: json.get("insert_max").unwrap().as_u64().unwrap() as usize,
pcap_file: json.get("pcap_file").unwrap().as_str().unwrap().to_owned(),
connection: format!(
"host={} user={} password={}",
json.get("database_host").unwrap().as_str().unwrap(),
json.get("database_user").unwrap().as_str().unwrap(),
json.get("database_password").unwrap().as_str().unwrap(),
),
device : json.get("parse_device").unwrap().as_str().unwrap().to_owned(),
is_device : json.get("from_device").unwrap().as_bool().unwrap(),
pcap_dir : json.get("pcap_dir").unwrap().as_str().unwrap().to_owned(),
device: json
.get("parse_device")
.unwrap()
.as_str()
.unwrap()
.to_owned(),
is_device: json.get("from_device").unwrap().as_bool().unwrap(),
pcap_dir: json.get("pcap_dir").unwrap().as_str().unwrap().to_owned(),
})
}

View File

@ -17,7 +17,7 @@ const PCAP_SIGNATURE: [u8; 4] = [0xed, 0xab, 0xee, 0xdb];
fn query_string(insert_max: &usize) -> String {
// Changed this to String with given capacity after stumbling on https://github.com/hoodie/concatenation_benchmarks-rs
// Changed this to String with given capacity after stumbling over https://github.com/hoodie/concatenation_benchmarks-rs
// Impressive performance increase!
let mut insert_template = String::with_capacity(insert_max * 8 + 43);
insert_template.push_str("INSERT INTO json_dump (packet) Values ");
@ -30,6 +30,11 @@ fn query_string(insert_max: &usize) -> String {
insert_template
}
fn smallest_prime_divisor(remainder: usize ) -> usize {
let smallest_divisor: usize = (2..(remainder/2)).into_par_iter().find_first(|x| remainder % x == 0).unwrap();
smallest_divisor
}
#[tokio::main(core_threads = 4)] // By default, tokio_postgres uses the tokio crate as its runtime.
async fn main() -> Result<(), Error> {
/* Init values from file */
@ -88,8 +93,15 @@ async fn main() -> Result<(), Error> {
match config.is_device {
false => for _pcap_file in pcap_map.keys() {
// TODO: Tuning vector capacity according to actuarial excpectation, mean average & std dev of packet size
let v: Vec<parser::QryData> = parser::parse(&_pcap_file, &config.filter);
//let mut v = Vec::<parser::QryData>::with_capacity(35536);
//v.extend(parser::parse(&_pcap_file, &config.filter));
let packets_serialized = serializer::serialize_packets(v);
//let mut packets_serialized = Vec::<serde_json::Value>::with_capacity(35536);
//packets_serialized.extend(serializer::serialize_packets(v));
/* Query */

View File

@ -3,7 +3,7 @@
"filter": "tcp && ip6",
"from_device": false,
"parse_device": "enp7s0",
"pcap_file": "../target/wohnung2.pcapng",
"pcap_file": "../target/arp_test.pcapng",
"pcap_dir": "../target",
"database_user": "postgres",
"database_host": "localhost",

View File

@ -12,6 +12,8 @@ const ETH_P_IPV6: usize = 0xDD86;
const ETH_P_IP: usize = 0x08;
const TCP: usize = 0x06;
const UDP: usize = 0x11;
const ETH_P_ARP: usize = 0x0608;
const ETH_P_RARP: usize = 0x3580;
fn build_ether() -> packet_handler::EtherHeader {
packet_handler::EtherHeader {
@ -31,6 +33,7 @@ pub struct QryData {
pub ipv6_header: Option<packet_handler::IpV6Header>,
pub tcp_header: Option<packet_handler::TcpHeader>,
pub udp_header: Option<packet_handler::UdpHeader>,
pub arp_header: Option<packet_handler::ArpHeader>,
pub reg_res: Option<String>,
}
@ -57,6 +60,7 @@ pub fn parse(parse_file: &std::path::Path, filter_str: &str) -> Vec<QryData> {
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>,
};
let mut v: Vec<QryData> = Vec::new();
@ -120,6 +124,9 @@ pub fn parse(parse_file: &std::path::Path, filter_str: &str) -> Vec<QryData> {
_ => println!("network protocol not implemented"),
}
}
ETH_P_ARP | ETH_P_RARP => {
me.arp_header = Some(packet_handler::arp_handler(packet.data)).unwrap();
}
_ => println!("network protocol not implemented"),
}
v.push(QryData {
@ -131,6 +138,7 @@ pub fn parse(parse_file: &std::path::Path, filter_str: &str) -> Vec<QryData> {
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,
});
}
@ -149,6 +157,7 @@ pub fn parse_device(parse_device: &str, filter_str: &str, insert_max: &usize) ->
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>,
};
let mut v: Vec<QryData> = Vec::new();
@ -212,6 +221,9 @@ pub fn parse_device(parse_device: &str, filter_str: &str, insert_max: &usize) ->
_ => println!("network protocol not implemented"),
}
}
ETH_P_ARP | ETH_P_RARP => {
me.arp_header = Some(packet_handler::arp_handler(packet.data)).unwrap();
}
_ => println!("network protocol not implemented"),
}
v.push(QryData {
@ -223,6 +235,7 @@ pub fn parse_device(parse_device: &str, filter_str: &str, insert_max: &usize) ->
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,
});

View File

@ -24,15 +24,13 @@ pub struct EtherHeader {
pub ether_type: i32,
}
// TODO: implement ethernet vlan shim header fields
// TODO: implement optional ethernet vlan shim header fields
pub fn ethernet_handler(packet_data: &[u8]) -> EtherHeader {
let mut _ether_dhost: [u8; ETH_ALEN] = [0; ETH_ALEN];
let mut _ether_shost: [u8; ETH_ALEN] = [0; ETH_ALEN];
let mut _ether_type: u16 = 0;
_ether_dhost.clone_from_slice(&packet_data[0..ETH_ALEN]);
//println!("{:?}", (&(_ether_dhost).to_owned()));
_ether_shost.clone_from_slice(&packet_data[ETH_ALEN..ETH_ALEN * 2]);
_ether_type = LittleEndian::read_u16(&packet_data[ETH_ALEN * 2..(ETH_ALEN * 2) + ETH_TLEN]);
@ -267,68 +265,107 @@ pub fn tcp_handler(ip_hlen: u32, packet_data: &[u8]) -> Option<TcpHeader> {
}
/* arp */
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArpHeader {
pub htype: u32,
pub ptype: u32,
pub hlen: u32,
pub plen: u32,
pub oper: u32,
pub sha: String,
pub spa: IpAddr,
pub tha: String,
pub tpa: IpAddr,
}
//#[derive(Debug, Clone, Serialize, Deserialize)]
//pub struct ArpHeader {
// pub htype: u32,
// pub ptype: u32,
// pub hlen: u32,
// pub plen: u32,
// pub oper: u32,
// pub sha: String,
// pub spa: IpAddr,
// pub tha: String,
// pub tpa: IpAddr,
//}
// u8, get_source_address, _: 103, 96, 4;
// u32, into Ipv4Addr, get_destination_address, _: 159, 128;
bitfield! {
struct BitfieldArpHeader ( MSB0 [u8] );
impl Debug;
u32;
get_htype, _: 0, 1;
get_ptype, _: 2, 3;
get_hlen, _: 4;
get_plen, _: 5;
get_oper, _: 6, 7;
get_sha, _: 8,13;
get_tha, _: 18, 23;
u8, get_spa, _: 17, 14, 4;
u32, into Ipv4Addr, get_tpa, _: 28, 24;
}
impl<T: AsRef<[u8]> + AsMut<[u8]>> BitfieldArpHeader<T> {
fn get_spa_as_ip_addr(&self) -> Ipv4Addr {
let mut src = [0; 4];
for (i, src) in src.iter_mut().enumerate() {
*src = self.get_spa(i);
}
src.into()
}
}
//bitfield! {
// struct BitfieldArpHeader ( MSB0 [u8] );
// impl Debug;
// u32;
// get_htype, _: 0, 1;
// get_ptype, _: 2, 3;
// get_hlen, _: 4;
// get_plen, _: 5;
// get_oper, _: 6, 7;
// get_sha, _: 8,13;
// get_tha, _: 18, 23;
// u8, get_spa, _: 17, 14, 4;
// u32, into Ipv4Addr, get_tpa, _: 28, 24;
//}
//
//impl<T: AsRef<[u8]> + AsMut<[u8]>> BitfieldArpHeader<T> {
// fn get_spa_as_ip_addr(&self) -> Ipv4Addr {
// let mut src = [0; 4];
// for (i, src) in src.iter_mut().enumerate() {
// *src = self.get_spa(i);
// }
// src.into()
// }
//}
// TODO: Fix this crap
//pub fn arp_handler(packet_data: &[u8]) -> Option<ArpHeader> {
// let mut raw_hdr: [u8; 28] = [0; 28];
// raw_hdr.copy_from_slice(&packet_data[ETHER_HDRLEN..ETHER_HDRLEN + 28]);
//
// let arp_header = BitfieldArpHeader(raw_hdr);
// let _sha: [u8; 6] = [0; 6];
// let _tha: [u8; 6] = [0; 6];
// raw_hdr[8..13].copy_from_slice(&_sha);
// raw_hdr[18..23].copy_from_slice(&_tha);
//
// Some(ArpHeader {
// htype: arp_header.get_htype(),
// ptype: arp_header.get_ptype(),
// hlen: arp_header.get_hlen().into(),
// plen: arp_header.get_plen().into(),
// oper: arp_header.get_oper(),
// sha: MacAddress::new(_sha as Eui48).to_hex_string(),
// //MacAddress::new(arp_header.get_sha() as Eui48).to_hex_string(),
// spa: IpAddr::V4(arp_header.get_spa_as_ip_addr()),
// tha: MacAddress::new(_tha as Eui48).to_hex_string(),
// tpa: IpAddr::V4(arp_header.get_tpa()),
// })
//}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct ArpHeader {
pub htype: u16,
pub ptype: u16,
pub hlen: u8,
pub plen: u8,
pub oper: u16,
pub sha: Eui48,
pub spa: IpAddr,
pub tha: Eui48,
pub tpa: IpAddr,
}
pub fn arp_handler(packet_data: &[u8]) -> Option<ArpHeader> {
let mut raw_hdr: [u8; 28] = [0; 28];
raw_hdr.copy_from_slice(&packet_data[ETHER_HDRLEN..ETHER_HDRLEN + 28]);
raw_hdr.copy_from_slice(
&packet_data[ETHER_HDRLEN .. ETHER_HDRLEN + 28]
);
let arp_header = BitfieldArpHeader(raw_hdr);
let _sha: [u8; 6] = [0; 6];
let _tha: [u8; 6] = [0; 6];
raw_hdr[8..13].copy_from_slice(&_sha);
raw_hdr[18..23].copy_from_slice(&_tha);
let mut _sha: [u8; 6] = [0; 6];
let mut _tha: [u8; 6] = [0; 6];
Some(ArpHeader {
htype: arp_header.get_htype(),
ptype: arp_header.get_ptype(),
hlen: arp_header.get_hlen().into(),
plen: arp_header.get_plen().into(),
oper: arp_header.get_oper(),
sha: MacAddress::new(_sha as Eui48).to_hex_string(),
//MacAddress::new(arp_header.get_sha() as Eui48).to_hex_string(),
spa: IpAddr::V4(arp_header.get_spa_as_ip_addr()),
tha: MacAddress::new(_tha as Eui48).to_hex_string(),
tpa: IpAddr::V4(arp_header.get_tpa()),
_sha.clone_from_slice(&raw_hdr[8..14]);
_tha.clone_from_slice(&raw_hdr[18..24]);
Some(ArpHeader{
htype: BigEndian::read_u16(&raw_hdr[0..2]),
ptype: BigEndian::read_u16(&raw_hdr[2..4]),
hlen: raw_hdr[4],
plen: raw_hdr[5],
oper: BigEndian::read_u16(&raw_hdr[6..8]),
//sha: MacAddress::new(_sha as Eui48).to_hex_string().to_owned(),
sha: _sha,
spa: IpAddr::V4(Ipv4Addr::from(BigEndian::read_u32(&raw_hdr[14..18]))),
//tha: MacAddress::new( _tha as Eui48 ).to_hex_string().to_owned(),
tha: _tha,
tpa: IpAddr::V4(Ipv4Addr::from(BigEndian::read_u32(&raw_hdr[24..28]))),
})
}

View File

@ -1,8 +1,8 @@
extern crate serde_json;
use crate::parser;
use rayon::prelude::*;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use serde_json::json;
use crate::parser;
impl Serialize for parser::QryData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -19,6 +19,7 @@ impl Serialize for parser::QryData {
state.serialize_field("ipv6_header", &self.ipv6_header)?;
state.serialize_field("tcp_header", &self.tcp_header)?;
state.serialize_field("udp_header", &self.udp_header)?;
state.serialize_field("arp_header", &self.arp_header)?;
state.serialize_field("data", &self.data)?;
state.serialize_field("reg_res", &self.reg_res)?;
state.end()