use std::net::{Ipv4Addr, Ipv6Addr};
use crate::counter_records::*;
use crate::datagram::*;
use crate::error::{ParseContext, ParseErrorKind};
use crate::flow_records::*;
use crate::samples::*;
use crate::*;
fn h(hex: &str) -> Vec<u8> {
let mut out = String::new();
for line in hex.lines() {
let t = line.trim();
if t.is_empty() {
continue;
}
let data = match t.find(" ") {
Some(pos) if pos >= 4 && t[..pos].bytes().all(|b| b.is_ascii_hexdigit()) => {
&t[pos..]
}
_ => t,
};
out.push_str(data);
}
hex::decode(out.replace(' ', "")).unwrap()
}
#[test]
fn test_parse_datagram_ipv4_agent() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
assert_eq!(result.datagrams.len(), 1);
let dg = &result.datagrams[0];
assert_eq!(dg.version, 5);
assert_eq!(
dg.agent_address,
AddressType::IPv4(Ipv4Addr::new(10, 0, 0, 1))
);
assert_eq!(dg.sub_agent_id, 0);
assert_eq!(dg.sequence_number, 1);
assert_eq!(dg.uptime, 1000);
assert_eq!(dg.samples.len(), 0);
}
#[test]
fn test_parse_datagram_ipv6_agent() {
let data = h("\
0000 00 00 00 05 00 00 00 02 fe 80 00 00 00 00 00 00\n\
0010 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01\n\
0020 00 00 03 e8 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
assert_eq!(result.datagrams.len(), 1);
let dg = &result.datagrams[0];
assert_eq!(dg.version, 5);
match &dg.agent_address {
AddressType::IPv6(addr) => {
assert_eq!(*addr, "fe80::1".parse::<Ipv6Addr>().unwrap());
}
_ => panic!("Expected IPv6 address"),
}
}
#[test]
fn test_parse_datagram_bad_version() {
let data = h("\
0000 00 00 00 04 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_some());
match result.error.unwrap() {
SflowError::UnsupportedVersion { version } => {
assert_eq!(version, 4)
}
other => panic!("Expected UnsupportedVersion, got {:?}", other),
}
}
#[test]
fn test_parse_truncated_input() {
let data = h("0000 00 00 00");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_some());
match result.error.unwrap() {
SflowError::Incomplete { .. } => {}
other => panic!("Expected Incomplete, got {:?}", other),
}
}
#[test]
fn test_parse_flow_sample_with_extended_switch() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 38 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 e9 00 00 00 10 00 00 00 64\n\
0050 00 00 00 00 00 00 00 c8 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let dg = &result.datagrams[0];
assert_eq!(dg.samples.len(), 1);
match &dg.samples[0] {
SflowSample::Flow(fs) => {
assert_eq!(fs.sequence_number, 1);
assert_eq!(fs.source_id_type, 0);
assert_eq!(fs.source_id_index, 3);
assert_eq!(fs.sampling_rate, 256);
assert_eq!(fs.records.len(), 1);
match &fs.records[0] {
FlowRecord::ExtendedSwitch(es) => {
assert_eq!(es.src_vlan, 100);
assert_eq!(es.dst_vlan, 200);
}
other => {
panic!("Expected ExtendedSwitch, got {:?}", other)
}
}
}
other => panic!("Expected Flow sample, got {:?}", other),
}
}
#[test]
fn test_parse_expanded_flow_sample() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 03\n\
0020 00 00 00 2c 00 00 00 01 00 00 00 00 00 00 00 05\n\
0030 00 00 02 00 00 00 07 d0 00 00 00 00 00 00 00 00\n\
0040 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
match &result.datagrams[0].samples[0] {
SflowSample::ExpandedFlow(efs) => {
assert_eq!(efs.source_id_type, 0);
assert_eq!(efs.source_id_index, 5);
assert_eq!(efs.sampling_rate, 512);
assert_eq!(efs.input_value, 1);
assert_eq!(efs.output_value, 2);
}
other => panic!("Expected ExpandedFlow, got {:?}", other),
}
}
#[test]
fn test_parse_counter_sample_with_vlan() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 02\n\
0020 00 00 00 30 00 00 00 01 00 00 00 01 00 00 00 01\n\
0030 00 00 00 05 00 00 00 1c 00 00 00 64 00 00 00 00\n\
0040 00 00 10 00 00 00 00 32 00 00 00 05 00 00 00 02\n\
0050 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
match &result.datagrams[0].samples[0] {
SflowSample::Counter(cs) => {
assert_eq!(cs.sequence_number, 1);
assert_eq!(cs.source_id_type, 0);
assert_eq!(cs.source_id_index, 1);
assert_eq!(cs.records.len(), 1);
match &cs.records[0] {
CounterRecord::Vlan(v) => {
assert_eq!(v.vlan_id, 100);
assert_eq!(v.octets, 4096);
assert_eq!(v.ucast_pkts, 50);
}
other => panic!("Expected Vlan, got {:?}", other),
}
}
other => panic!("Expected Counter sample, got {:?}", other),
}
}
#[test]
fn test_parse_expanded_counter_sample() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 04\n\
0020 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 07\n\
0030 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
match &result.datagrams[0].samples[0] {
SflowSample::ExpandedCounter(ecs) => {
assert_eq!(ecs.source_id_type, 0);
assert_eq!(ecs.source_id_index, 7);
}
other => panic!("Expected ExpandedCounter, got {:?}", other),
}
}
#[test]
fn test_parse_raw_packet_header() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 3c 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 00 01 00 00 00 14 00 00 00 01\n\
0050 00 00 00 64 00 00 00 00 00 00 00 04 aa bb cc dd\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::RawPacketHeader(rph) => {
assert_eq!(rph.header_protocol, 1);
assert_eq!(rph.frame_length, 100);
assert_eq!(rph.header_length, 4);
assert_eq!(rph.header, vec![0xAA, 0xBB, 0xCC, 0xDD]);
}
other => panic!("Expected RawPacketHeader, got {:?}", other),
}
}
#[test]
fn test_parse_sampled_ipv4() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 48 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 00 03 00 00 00 20 00 00 00 64\n\
0050 00 00 00 06 c0 a8 01 01 0a 00 00 01 00 00 00 50\n\
0060 00 00 01 bb 00 00 00 02 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::SampledIpv4(s) => {
assert_eq!(s.protocol, 6);
assert_eq!(s.src_ip, Ipv4Addr::new(192, 168, 1, 1));
assert_eq!(s.dst_ip, Ipv4Addr::new(10, 0, 0, 1));
assert_eq!(s.src_port, 80);
assert_eq!(s.dst_port, 443);
}
other => panic!("Expected SampledIpv4, got {:?}", other),
}
}
#[test]
fn test_parse_sampled_ipv6() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 60 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 00 04 00 00 00 38 00 00 00 c8\n\
0050 00 00 00 11 20 01 0d b8 00 00 00 00 00 00 00 00\n\
0060 00 00 00 01 20 01 0d b8 00 00 00 00 00 00 00 00\n\
0070 00 00 00 02 00 00 04 d2 00 00 16 2e 00 00 00 00\n\
0080 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::SampledIpv6(s) => {
assert_eq!(s.protocol, 17);
assert_eq!(s.src_ip, Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
assert_eq!(s.dst_ip, Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2));
assert_eq!(s.src_port, 1234);
assert_eq!(s.dst_port, 5678);
}
other => panic!("Expected SampledIpv6, got {:?}", other),
}
}
#[test]
fn test_parse_extended_router() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 38 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 ea 00 00 00 10 00 00 00 01\n\
0050 0a 00 00 fe 00 00 00 18 00 00 00 10\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::ExtendedRouter(er) => {
assert_eq!(er.next_hop, AddressType::IPv4(Ipv4Addr::new(10, 0, 0, 254)));
assert_eq!(er.src_mask_len, 24);
assert_eq!(er.dst_mask_len, 16);
}
other => panic!("Expected ExtendedRouter, got {:?}", other),
}
}
#[test]
fn test_parse_extended_gateway() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 58 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 eb 00 00 00 30 00 00 00 01\n\
0050 0a 00 00 01 00 00 fd e8 00 00 fd e8 00 00 fd e9\n\
0060 00 00 00 01 00 00 00 02 00 00 00 02 00 00 fd e9\n\
0070 00 00 fd ea 00 00 00 01 ff ff 00 01\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::ExtendedGateway(eg) => {
assert_eq!(eg.as_number, 65000);
assert_eq!(eg.src_peer_as, 65001);
assert_eq!(eg.as_path_segments.len(), 1);
assert_eq!(eg.as_path_segments[0].segment_type, 2);
assert_eq!(eg.as_path_segments[0].values, vec![65001, 65002]);
assert_eq!(eg.communities, vec![0xFFFF0001]);
}
other => panic!("Expected ExtendedGateway, got {:?}", other),
}
}
#[test]
fn test_parse_extended_user() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 44 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 ec 00 00 00 1c 00 00 00 00\n\
0050 00 00 00 05 61 64 6d 69 6e 00 00 00 00 00 00 00\n\
0060 00 00 00 04 72 6f 6f 74\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::ExtendedUser(eu) => {
assert_eq!(eu.src_user, "admin");
assert_eq!(eu.dst_user, "root");
}
other => panic!("Expected ExtendedUser, got {:?}", other),
}
}
#[test]
fn test_parse_extended_url() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 4c 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 ed 00 00 00 24 00 00 00 01\n\
0050 00 00 00 0b 2f 69 6e 64 65 78 2e 68 74 6d 6c 00\n\
0060 00 00 00 0b 65 78 61 6d 70 6c 65 2e 63 6f 6d 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::ExtendedUrl(eu) => {
assert_eq!(eu.direction, 1);
assert_eq!(eu.url, "/index.html");
assert_eq!(eu.host, "example.com");
}
other => panic!("Expected ExtendedUrl, got {:?}", other),
}
}
#[test]
fn test_parse_generic_interface() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 02\n\
0020 00 00 00 6c 00 00 00 01 00 00 00 01 00 00 00 01\n\
0030 00 00 00 01 00 00 00 58 00 00 00 01 00 00 00 06\n\
0040 00 00 00 00 3b 9a ca 00 00 00 00 01 00 00 00 03\n\
0050 00 00 00 00 00 0f 42 40 00 00 01 f4 00 00 00 0a\n\
0060 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00\n\
0070 00 00 00 00 00 1e 84 80 00 00 02 58 00 00 00 14\n\
0080 00 00 00 08 00 00 00 01 00 00 00 00 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let cs = match &result.datagrams[0].samples[0] {
SflowSample::Counter(cs) => cs,
other => panic!("Expected Counter, got {:?}", other),
};
match &cs.records[0] {
CounterRecord::GenericInterface(gi) => {
assert_eq!(gi.if_index, 1);
assert_eq!(gi.if_type, 6);
assert_eq!(gi.if_speed, 1_000_000_000);
assert_eq!(gi.if_in_octets, 1_000_000);
assert_eq!(gi.if_out_octets, 2_000_000);
assert_eq!(gi.if_in_ucast_pkts, 500);
}
other => {
panic!("Expected GenericInterface, got {:?}", other)
}
}
}
#[test]
fn test_parse_ethernet_interface() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 02\n\
0020 00 00 00 48 00 00 00 01 00 00 00 01 00 00 00 01\n\
0030 00 00 00 02 00 00 00 34 00 00 00 00 00 00 00 01\n\
0040 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05\n\
0050 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 09\n\
0060 00 00 00 0a 00 00 00 0b 00 00 00 0c\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let cs = match &result.datagrams[0].samples[0] {
SflowSample::Counter(cs) => cs,
other => panic!("Expected Counter, got {:?}", other),
};
match &cs.records[0] {
CounterRecord::EthernetInterface(ei) => {
assert_eq!(ei.dot3_stats_alignment_errors, 0);
assert_eq!(ei.dot3_stats_fcs_errors, 1);
assert_eq!(ei.dot3_stats_symbol_errors, 12);
}
other => {
panic!("Expected EthernetInterface, got {:?}", other)
}
}
}
#[test]
fn test_parse_processor() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 02\n\
0020 00 00 00 30 00 00 00 01 00 00 00 01 00 00 00 01\n\
0030 00 00 03 e9 00 00 00 1c 00 00 00 0a 00 00 00 0f\n\
0040 00 00 00 0c 00 00 00 01 dc d6 50 00 00 00 00 00\n\
0050 ee 6b 28 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let cs = match &result.datagrams[0].samples[0] {
SflowSample::Counter(cs) => cs,
other => panic!("Expected Counter, got {:?}", other),
};
match &cs.records[0] {
CounterRecord::Processor(p) => {
assert_eq!(p.cpu_5s, 10);
assert_eq!(p.cpu_1m, 15);
assert_eq!(p.cpu_5m, 12);
assert_eq!(p.total_memory, 8_000_000_000);
assert_eq!(p.free_memory, 4_000_000_000);
}
other => panic!("Expected Processor, got {:?}", other),
}
}
#[test]
fn test_unknown_sample_type() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 06 30 01\n\
0020 00 00 00 04 de ad be ef\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
match &result.datagrams[0].samples[0] {
SflowSample::Unknown {
enterprise,
format,
data,
} => {
assert_eq!(*enterprise, 99);
assert_eq!(*format, 1);
assert_eq!(data, &[0xDE, 0xAD, 0xBE, 0xEF]);
}
other => panic!("Expected Unknown sample, got {:?}", other),
}
}
#[test]
fn test_unknown_flow_record() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 2c 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 e7 00 00 00 04 01 02 03 04\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::Unknown {
enterprise,
format,
data,
} => {
assert_eq!(*enterprise, 0);
assert_eq!(*format, 999);
assert_eq!(data, &[0x01, 0x02, 0x03, 0x04]);
}
other => panic!("Expected Unknown record, got {:?}", other),
}
}
#[test]
fn test_unknown_counter_record() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 02\n\
0020 00 00 00 18 00 00 00 01 00 00 00 01 00 00 00 01\n\
0030 00 00 50 2a 00 00 00 04 01 02 03 04\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let cs = match &result.datagrams[0].samples[0] {
SflowSample::Counter(cs) => cs,
other => panic!("Expected Counter, got {:?}", other),
};
match &cs.records[0] {
CounterRecord::Unknown {
enterprise,
format,
data,
} => {
assert_eq!(*enterprise, 5);
assert_eq!(*format, 42);
assert_eq!(data, &[0x01, 0x02, 0x03, 0x04]);
}
other => panic!("Expected Unknown record, got {:?}", other),
}
}
#[test]
fn test_max_samples_limit() {
let mut data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 02\
");
let empty_flow = h("\
0000 00 00 00 01 00 00 00 20 00 00 00 01 00 00 00 03\n\
0010 00 00 01 00 00 00 03 e8 00 00 00 00 00 00 00 01\n\
0020 00 00 00 02 00 00 00 00\
");
data.extend_from_slice(&empty_flow);
data.extend_from_slice(&empty_flow);
let parser = SflowParser::builder().with_max_samples(1).build();
let result = parser.parse_bytes(&data);
assert!(result.error.is_some());
match result.error.unwrap() {
SflowError::TooManySamples { count, max } => {
assert_eq!(count, 2);
assert_eq!(max, 1);
}
other => panic!("Expected TooManySamples, got {:?}", other),
}
}
#[test]
fn test_empty_input() {
let parser = SflowParser::default();
let result = parser.parse_bytes(&[]);
assert!(result.error.is_none());
assert_eq!(result.datagrams.len(), 0);
}
#[test]
fn test_parse_result_serialization() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
let json = serde_json::to_string(&result).unwrap();
let deserialized: ParseResult = serde_json::from_str(&json).unwrap();
assert_eq!(result, deserialized);
}
#[test]
fn test_parse_sampled_ethernet() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 40 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 00 02 00 00 00 18 00 00 00 40\n\
0050 aa bb cc dd ee ff 00 00 11 22 33 44 55 66 00 00\n\
0060 00 00 08 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::SampledEthernet(se) => {
assert_eq!(se.length, 64);
assert_eq!(se.src_mac.to_string(), "AA:BB:CC:DD:EE:FF");
assert_eq!(se.dst_mac.to_string(), "11:22:33:44:55:66");
assert_eq!(se.eth_type, 0x0800);
}
other => panic!("Expected SampledEthernet, got {:?}", other),
}
}
#[test]
fn test_parse_error_display() {
let err = SflowError::ParseError {
offset: 42,
context: ParseContext::FlowSample,
kind: ParseErrorKind::NomError(nom::error::ErrorKind::Eof),
};
let msg = format!("{}", err);
assert!(msg.contains("42"));
assert!(msg.contains("flow sample"));
assert!(msg.contains("Eof"));
}
#[test]
fn test_invalid_address_type() {
let data = h("\
0000 00 00 00 05 00 00 00 03 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_some());
match result.error.unwrap() {
SflowError::ParseError { context, kind, .. } => {
assert_eq!(context, ParseContext::AgentAddress);
assert_eq!(kind, ParseErrorKind::InvalidAddressType);
}
other => panic!("Expected ParseError for address, got {:?}", other),
}
}
#[test]
fn test_multi_datagram_buffer() {
let single = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 00\
");
let mut data = single.clone();
data.extend_from_slice(&single);
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
assert_eq!(result.datagrams.len(), 2);
assert_eq!(result.datagrams[0].sequence_number, 1);
assert_eq!(result.datagrams[1].sequence_number, 1);
}
#[test]
fn test_extended_gateway_empty_segments() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 44 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 eb 00 00 00 1c 00 00 00 01\n\
0050 0a 00 00 01 00 00 fd e8 00 00 fd e8 00 00 fd e9\n\
0060 00 00 00 00 00 00 00 00\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::ExtendedGateway(eg) => {
assert_eq!(eg.as_number, 65000);
assert_eq!(eg.as_path_segments.len(), 0);
assert_eq!(eg.communities.len(), 0);
}
other => panic!("Expected ExtendedGateway, got {:?}", other),
}
}
#[test]
fn test_extended_router_ipv6_next_hop() {
let data = h("\
0000 00 00 00 05 00 00 00 01 0a 00 00 01 00 00 00 00\n\
0010 00 00 00 01 00 00 03 e8 00 00 00 01 00 00 00 01\n\
0020 00 00 00 44 00 00 00 01 00 00 00 03 00 00 01 00\n\
0030 00 00 03 e8 00 00 00 00 00 00 00 01 00 00 00 02\n\
0040 00 00 00 01 00 00 03 ea 00 00 00 1c 00 00 00 02\n\
0050 fe 80 00 00 00 00 00 00 00 00 00 00 00 00 00 01\n\
0060 00 00 00 30 00 00 00 18\
");
let parser = SflowParser::default();
let result = parser.parse_bytes(&data);
assert!(result.error.is_none());
let fs = match &result.datagrams[0].samples[0] {
SflowSample::Flow(fs) => fs,
other => panic!("Expected Flow, got {:?}", other),
};
match &fs.records[0] {
FlowRecord::ExtendedRouter(er) => {
match &er.next_hop {
AddressType::IPv6(addr) => {
assert_eq!(*addr, "fe80::1".parse::<Ipv6Addr>().unwrap());
}
other => panic!("Expected IPv6 next-hop, got {:?}", other),
}
assert_eq!(er.src_mask_len, 48);
assert_eq!(er.dst_mask_len, 24);
}
other => panic!("Expected ExtendedRouter, got {:?}", other),
}
}