ipfix_parser/models/enterprise.rs
1//! Enterprise-specific Information Element definitions
2//!
3//! The type of an enterprise-specific Information Element is not in the IANA
4//! registry. It can come from two sources, see [`ElementSource`]:
5//!
6//! - a definition registered by the caller (e.g. from vendor documentation),
7//! with [`Decoder::register_enterprise_element`](crate::Decoder::register_enterprise_element);
8//! - an Information Element type record sent by the Exporter
9//! ([RFC 5610](https://www.rfc-editor.org/rfc/rfc5610)).
10
11use crate::registry::{DataType, DataTypeSemantics};
12
13/// Where an enterprise-specific Information Element definition comes from
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub enum ElementSource {
17 /// Registered by the caller. This is the Collecting Process's "own
18 /// internal definition": it always takes precedence over type records
19 /// (RFC 5610 Section 3.9).
20 Registered,
21 /// Learned from an Information Element type record sent by the Exporter
22 /// (RFC 5610), scoped to the Transport Session and Observation Domain.
23 TypeRecord,
24}
25
26/// Type information of an enterprise-specific Information Element
27/// (RFC 5610 Section 3)
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct EnterpriseElement {
31 /// Private Enterprise Number (`privateEnterpriseNumber`, 346)
32 pub enterprise_number: u32,
33 /// Information Element identifier (`informationElementId`, 303)
34 pub id: u16,
35 /// Name (`informationElementName`, 341)
36 pub name: Option<String>,
37 /// Abstract data type (`informationElementDataType`, 339)
38 pub data_type: DataType,
39 /// Data type semantics (`informationElementSemantics`, 344)
40 pub semantics: Option<DataTypeSemantics>,
41 /// Units, as a value of the IANA "IPFIX Information Element Units"
42 /// registry (`informationElementUnits`, 345)
43 pub units: Option<u16>,
44 /// Inclusive low end of the range of acceptable values
45 /// (`informationElementRangeBegin`, 342)
46 pub range_begin: Option<u64>,
47 /// Inclusive high end of the range of acceptable values
48 /// (`informationElementRangeEnd`, 343)
49 pub range_end: Option<u64>,
50 /// Human-readable description (`informationElementDescription`, 340)
51 pub description: Option<String>,
52 /// Where the definition comes from
53 pub source: ElementSource,
54}
55
56impl EnterpriseElement {
57 /// A caller-registered definition with a name and a data type
58 pub fn new(enterprise_number: u32, id: u16, name: &str, data_type: DataType) -> Self {
59 Self {
60 enterprise_number,
61 id,
62 name: Some(name.to_string()),
63 data_type,
64 semantics: None,
65 units: None,
66 range_begin: None,
67 range_end: None,
68 description: None,
69 source: ElementSource::Registered,
70 }
71 }
72
73 /// Set the data type semantics
74 pub fn with_semantics(mut self, semantics: DataTypeSemantics) -> Self {
75 self.semantics = Some(semantics);
76 self
77 }
78
79 /// Whether the data type and semantics combination is valid
80 /// (RFC 5610 Section 3.10). Invalid combinations MUST be ignored.
81 ///
82 /// RFC 5610 predates the structured data types (RFC 6313) and
83 /// `unsigned256` (RFC 9740): lists accept the `list` semantics, and
84 /// `unsigned256` follows the other unsigned types.
85 pub fn has_valid_semantics(&self) -> bool {
86 let Some(semantics) = self.semantics else {
87 return true;
88 };
89 use DataType::*;
90 use DataTypeSemantics as S;
91 match self.data_type {
92 Unsigned8 | Unsigned16 | Unsigned32 | Unsigned64 | Unsigned256 => true,
93 Signed8 | Signed16 | Signed32 | Signed64 => semantics != S::Flags,
94 Float32 | Float64 => !matches!(semantics, S::Identifier | S::Flags),
95 BasicList | SubTemplateList | SubTemplateMultiList => {
96 matches!(semantics, S::Default | S::List)
97 }
98 _ => semantics == S::Default,
99 }
100 }
101}