Skip to main content

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}