use super::types::{IeAbstractType, InformationElement};
const fn ie(
id: u16,
name: &'static str,
abstract_type: IeAbstractType,
units: Option<&'static str>,
description: &'static str,
) -> InformationElement {
InformationElement {
id,
name,
abstract_type,
units,
description,
}
}
pub const IES: &[InformationElement] = &[
ie(
1,
"octetDeltaCount",
IeAbstractType::Unsigned64,
Some("octets"),
"Number of octets observed for this Flow since the previous report.",
),
ie(
2,
"packetDeltaCount",
IeAbstractType::Unsigned64,
Some("packets"),
"Number of incoming packets observed for this Flow since the previous report.",
),
ie(
4,
"protocolIdentifier",
IeAbstractType::Unsigned8,
None,
"IPv4/IPv6 protocol number (e.g. 6 = TCP, 17 = UDP).",
),
ie(
5,
"ipClassOfService",
IeAbstractType::Unsigned8,
None,
"IPv4 TOS byte or the equivalent IPv6 Traffic Class field.",
),
ie(
6,
"tcpControlBits",
IeAbstractType::Unsigned16,
None,
"Cumulative OR of all TCP-header control flags observed in the Flow (RFC 7125).",
),
ie(
7,
"sourceTransportPort",
IeAbstractType::Unsigned16,
None,
"Source TCP/UDP/SCTP port number.",
),
ie(
8,
"sourceIPv4Address",
IeAbstractType::Ipv4Address,
None,
"Source IPv4 address.",
),
ie(
10,
"ingressInterface",
IeAbstractType::Unsigned32,
None,
"Index of the IP interface where the packets were received.",
),
ie(
11,
"destinationTransportPort",
IeAbstractType::Unsigned16,
None,
"Destination TCP/UDP/SCTP port number.",
),
ie(
12,
"destinationIPv4Address",
IeAbstractType::Ipv4Address,
None,
"Destination IPv4 address.",
),
ie(
14,
"egressInterface",
IeAbstractType::Unsigned32,
None,
"Index of the IP interface where the packets were sent.",
),
ie(
23,
"postOctetDeltaCount",
IeAbstractType::Unsigned64,
Some("octets"),
"Octets observed after egress processing (post-NAT, post-traffic-shaper).",
),
ie(
24,
"postPacketDeltaCount",
IeAbstractType::Unsigned64,
Some("packets"),
"Packets observed after egress processing.",
),
ie(
27,
"sourceIPv6Address",
IeAbstractType::Ipv6Address,
None,
"Source IPv6 address.",
),
ie(
28,
"destinationIPv6Address",
IeAbstractType::Ipv6Address,
None,
"Destination IPv6 address.",
),
ie(
56,
"sourceMacAddress",
IeAbstractType::MacAddress,
None,
"L2 source MAC address (incoming).",
),
ie(
58,
"vlanId",
IeAbstractType::Unsigned16,
None,
"VLAN identifier observed on the ingress side (802.1Q).",
),
ie(
59,
"postVlanId",
IeAbstractType::Unsigned16,
None,
"VLAN identifier observed on the egress side after any 802.1Q rewrite.",
),
ie(
80,
"destinationMacAddress",
IeAbstractType::MacAddress,
None,
"L2 destination MAC address (incoming).",
),
ie(
85,
"octetTotalCount",
IeAbstractType::Unsigned64,
Some("octets"),
"Total octets observed for this Flow over its lifetime.",
),
ie(
86,
"packetTotalCount",
IeAbstractType::Unsigned64,
Some("packets"),
"Total packets observed for this Flow over its lifetime.",
),
ie(
95,
"applicationId",
IeAbstractType::OctetArray,
None,
"Vendor-defined application identifier (Cisco NBAR2, Palo Alto App-ID, etc.).",
),
ie(
96,
"applicationName",
IeAbstractType::String,
None,
"Human-readable application name corresponding to applicationId.",
),
ie(
136,
"flowEndReason",
IeAbstractType::Unsigned8,
None,
"Reason the Flow ended: 1=idle, 2=active, 3=end-detected, 4=forced, 5=resources.",
),
ie(
152,
"flowStartMilliseconds",
IeAbstractType::DateTimeMilliseconds,
Some("ms"),
"Absolute timestamp of the Flow's first packet.",
),
ie(
153,
"flowEndMilliseconds",
IeAbstractType::DateTimeMilliseconds,
Some("ms"),
"Absolute timestamp of the Flow's last packet.",
),
ie(
154,
"flowStartMicroseconds",
IeAbstractType::DateTimeMicroseconds,
Some("us"),
"Absolute timestamp of the Flow's first packet (microsecond precision).",
),
ie(
155,
"flowEndMicroseconds",
IeAbstractType::DateTimeMicroseconds,
Some("us"),
"Absolute timestamp of the Flow's last packet (microsecond precision).",
),
ie(
239,
"biflowDirection",
IeAbstractType::Unsigned8,
None,
"Direction marker for a biflow (RFC 5103): 1=initiator, 2=reverseInitiator, 3=perimeter.",
),
ie(
351,
"layer2SegmentId",
IeAbstractType::Unsigned64,
None,
"L2 segment identifier (RFC 7637; VxLAN VNI, NVGRE VSID, etc.).",
),
];
pub fn lookup_by_id(id: u16) -> Option<&'static InformationElement> {
IES.binary_search_by_key(&id, |e| e.id)
.ok()
.map(|i| &IES[i])
}
pub fn lookup_by_name(name: &str) -> Option<&'static InformationElement> {
IES.iter().find(|e| e.name == name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_is_sorted_by_id() {
let mut prev: i32 = -1;
for ie in IES.iter() {
assert!(
(ie.id as i32) > prev,
"IE registry not sorted at id {}",
ie.id
);
prev = ie.id as i32;
}
}
#[test]
fn lookup_by_id_finds_known_entries() {
let port = lookup_by_id(7).unwrap();
assert_eq!(port.name, "sourceTransportPort");
assert_eq!(port.abstract_type, IeAbstractType::Unsigned16);
let v4 = lookup_by_id(8).unwrap();
assert_eq!(v4.name, "sourceIPv4Address");
assert_eq!(v4.abstract_type, IeAbstractType::Ipv4Address);
let cause = lookup_by_id(136).unwrap();
assert_eq!(cause.name, "flowEndReason");
}
#[test]
fn lookup_by_id_misses_unknown() {
assert!(lookup_by_id(0).is_none());
assert!(lookup_by_id(9999).is_none());
}
#[test]
fn lookup_by_name_finds_known() {
assert_eq!(lookup_by_name("octetTotalCount").map(|e| e.id), Some(85));
assert_eq!(lookup_by_name("tcpControlBits").map(|e| e.id), Some(6));
}
#[test]
fn lookup_by_name_is_case_sensitive() {
assert!(lookup_by_name("OctetTotalCount").is_none());
assert!(lookup_by_name("octettotalcount").is_none());
}
#[test]
fn iana_required_ies_present() {
for required in &[6, 136] {
assert!(
lookup_by_id(*required).is_some(),
"IE {} missing from registry",
required
);
}
}
#[test]
fn unit_field_on_counter_ies() {
assert_eq!(lookup_by_id(1).unwrap().units, Some("octets"));
assert_eq!(lookup_by_id(2).unwrap().units, Some("packets"));
assert_eq!(lookup_by_id(85).unwrap().units, Some("octets"));
}
}