Skip to main content

ipfix_parser/models/
value.rs

1//! IPFIX field values
2//!
3//! Decoded Information Element values, following the abstract data types of
4//! [RFC 7012 Section 3.1](https://www.rfc-editor.org/rfc/rfc7012#section-3.1)
5//! encoded as per [RFC 7011 Section 6](https://www.rfc-editor.org/rfc/rfc7011#section-6),
6//! and the structured data types of [RFC 6313](https://www.rfc-editor.org/rfc/rfc6313).
7
8use super::message::DataRecord;
9use std::net::{Ipv4Addr, Ipv6Addr};
10
11/// MAC address (6 bytes) - RFC 7011 Section 6.1.2
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct MacAddress(pub [u8; 6]);
15
16impl MacAddress {
17    /// Create a new MAC address from 6 bytes
18    pub const fn new(bytes: [u8; 6]) -> Self {
19        Self(bytes)
20    }
21
22    /// Get the MAC address as a byte array
23    pub const fn as_bytes(&self) -> &[u8; 6] {
24        &self.0
25    }
26}
27
28impl std::fmt::Display for MacAddress {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(
31            f,
32            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
33            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
34        )
35    }
36}
37
38/// NTP Timestamp - RFC 7011 Sections 6.1.9 and 6.1.10
39///
40/// Used by `dateTimeMicroseconds` and `dateTimeNanoseconds`: seconds since the
41/// NTP epoch (1 January 1900) and a binary fraction of a second. For
42/// `dateTimeMicroseconds`, the lower 11 bits of the fraction SHOULD be zero.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub struct NtpTimestamp {
46    /// Seconds since 1 January 1900 00:00 UTC
47    pub seconds: u32,
48    /// Fraction of a second, in units of 2^-32 seconds
49    pub fraction: u32,
50}
51
52impl NtpTimestamp {
53    /// Offset between the NTP epoch (1900) and the UNIX epoch (1970), in seconds
54    pub const UNIX_EPOCH_OFFSET: u64 = 2_208_988_800;
55
56    /// Nanoseconds since the UNIX epoch, or `None` if the timestamp is before
57    /// 1970. Note that RFC 7011 Section 5.2 does not handle the NTP era
58    /// wraparound (2036); this conversion assumes era 0.
59    pub fn unix_nanos(&self) -> Option<u64> {
60        let secs = (self.seconds as u64).checked_sub(Self::UNIX_EPOCH_OFFSET)?;
61        let nanos = ((self.fraction as u64) * 1_000_000_000) >> 32;
62        Some(secs * 1_000_000_000 + nanos)
63    }
64}
65
66/// Structured data type semantic - RFC 6313 Section 4.4
67///
68/// IANA "IPFIX Structured Data Types Semantics" registry.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71pub enum ListSemantic {
72    /// 0x00 - none of the elements are actual properties of the Data Record
73    NoneOf,
74    /// 0x01 - only a single element is an actual property of the Data Record
75    ExactlyOneOf,
76    /// 0x02 - one or more elements are actual properties of the Data Record
77    OneOrMoreOf,
78    /// 0x03 - all elements are actual properties of the Data Record
79    AllOf,
80    /// 0x04 - all elements are actual properties of the Data Record, in order
81    Ordered,
82    /// 0xFF - the semantic is not specified
83    Undefined,
84    /// Unassigned value
85    Unassigned(u8),
86}
87
88impl From<u8> for ListSemantic {
89    fn from(value: u8) -> Self {
90        match value {
91            0x00 => ListSemantic::NoneOf,
92            0x01 => ListSemantic::ExactlyOneOf,
93            0x02 => ListSemantic::OneOrMoreOf,
94            0x03 => ListSemantic::AllOf,
95            0x04 => ListSemantic::Ordered,
96            0xFF => ListSemantic::Undefined,
97            other => ListSemantic::Unassigned(other),
98        }
99    }
100}
101
102impl ListSemantic {
103    /// The wire value of the semantic
104    pub fn value(&self) -> u8 {
105        match self {
106            ListSemantic::NoneOf => 0x00,
107            ListSemantic::ExactlyOneOf => 0x01,
108            ListSemantic::OneOrMoreOf => 0x02,
109            ListSemantic::AllOf => 0x03,
110            ListSemantic::Ordered => 0x04,
111            ListSemantic::Undefined => 0xFF,
112            ListSemantic::Unassigned(v) => *v,
113        }
114    }
115}
116
117/// basicList - RFC 6313 Section 4.5.1
118///
119/// ```text
120///  0                   1                   2                   3
121///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
122/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
123/// |   Semantic    |0|          Field ID           |   Element...  |
124/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
125/// | ...Length     |           basicList Content ...               |
126/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
127/// |                              ...                              |
128/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
129/// |                              ...                              |
130/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
131/// ```
132///
133/// If the Enterprise bit of the Field ID is set, a four-byte Enterprise Number
134/// follows the Element Length (RFC 6313 Figure 2).
135#[derive(Debug, Clone, PartialEq)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137pub struct BasicList {
138    /// Relationship among the list elements
139    pub semantic: ListSemantic,
140    /// Information Element identifier of the list elements (Enterprise bit
141    /// removed)
142    pub field_id: u16,
143    /// Length of each element, or 65535 if variable-length
144    pub element_length: u16,
145    /// Private Enterprise Number, present if the Enterprise bit is set
146    pub enterprise_number: Option<u32>,
147    /// Decoded list elements
148    pub basic_list_content: Vec<FieldValue>,
149}
150
151/// subTemplateList - RFC 6313 Section 4.5.2
152///
153/// ```text
154///  0                   1                   2                   3
155///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
156/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
157/// |   Semantic    |         Template ID           |     ...       |
158/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
159/// |                subTemplateList Content    ...                 |
160/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
161/// |                              ...                              |
162/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
163/// ```
164#[derive(Debug, Clone, PartialEq)]
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
166pub struct SubTemplateList {
167    /// Relationship among the Data Records
168    pub semantic: ListSemantic,
169    /// Template used to decode the content
170    pub template_id: u16,
171    /// Decoded Data Records
172    pub sub_template_list_content: Vec<DataRecord>,
173}
174
175/// subTemplateMultiList - RFC 6313 Section 4.5.3
176///
177/// ```text
178///  0                   1                   2                   3
179///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
180/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181/// |    Semantic   |         Template ID X         |Data Records...|
182/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
183/// | ... Length X  |        Data Record X.1 Content ...            |
184/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
185/// |                              ...                              |
186/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
187/// |      ...      |        Data Record X.2 Content ...            |
188/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
189/// |                              ...                              |
190/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
191/// |      ...      |        Data Record X.L Content ...            |
192/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
193/// |                              ...                              |
194/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
195/// |      ...      |         Template ID Y         |Data Records...|
196/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
197/// | ... Length Y  |        Data Record  Y.1 Content ...           |
198/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
199/// |                              ...                              |
200/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
201/// |      ...      |        Data Record Y.2 Content ...            |
202/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
203/// |                              ...                              |
204/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
205/// |      ...      |        Data Record Y.M Content ...            |
206/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
207/// |                              ...                              |
208/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
209/// |      ...      |         Template ID Z         |Data Records...|
210/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
211/// | ... Length Z  |        Data Record Z.1 Content ...            |
212/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
213/// |                              ...                              |
214/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
215/// |      ...      |        Data Record Z.2 Content ...            |
216/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
217/// |                              ...                              |
218/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
219/// |      ...      |        Data Record Z.N Content ...            |
220/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
221/// |                              ...                              |
222/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
223/// |      ...      |
224/// +-+-+-+-+-+-+-+-+
225/// ```
226#[derive(Debug, Clone, PartialEq)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct SubTemplateMultiList {
229    /// Top-level relationship among the series of Data Records
230    pub semantic: ListSemantic,
231    /// One entry per Template ID / Data Records Length block
232    pub entries: Vec<SubTemplateMultiListEntry>,
233}
234
235/// One Template ID / Data Records block of a subTemplateMultiList
236/// (RFC 6313 Section 4.5.3)
237#[derive(Debug, Clone, PartialEq)]
238#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
239pub struct SubTemplateMultiListEntry {
240    /// Template used to decode the Data Records of this entry
241    pub template_id: u16,
242    /// Total length of this entry, including the Template ID and the Data
243    /// Records Length fields (so at least 4, RFC 6313 Errata 3232)
244    pub data_records_length: u16,
245    /// Decoded Data Records
246    pub data_records: Vec<DataRecord>,
247}
248
249/// A decoded Information Element value
250///
251/// The variant is chosen from the abstract data type of the Information
252/// Element in the IANA registry. Values that cannot be decoded with that type
253/// (unknown or enterprise-specific Information Element, length not allowed for
254/// the type, invalid UTF-8, unknown Template in a subTemplateList, ...) are
255/// kept as [`FieldValue::Raw`] rather than failing the whole Message.
256#[derive(Debug, Clone, PartialEq)]
257#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
258pub enum FieldValue {
259    /// octetArray
260    OctetArray(Vec<u8>),
261    /// unsigned8, unsigned16, unsigned32, unsigned64 (possibly reduced-size)
262    Unsigned(u64),
263    /// unsigned256 (RFC 9740), big-endian, left-padded with zeros if
264    /// reduced-size
265    Unsigned256([u8; 32]),
266    /// signed8, signed16, signed32, signed64 (possibly reduced-size, sign
267    /// extended as per RFC 7011 Errata 7875)
268    Signed(i64),
269    /// float32
270    Float32(f32),
271    /// float64 (possibly reduced-size to float32)
272    Float64(f64),
273    /// boolean (RFC 7011 Section 6.1.5: 1 = true, 2 = false)
274    Boolean(bool),
275    /// macAddress
276    MacAddress(MacAddress),
277    /// string (UTF-8)
278    String(String),
279    /// dateTimeSeconds: seconds since the UNIX epoch
280    DateTimeSeconds(u32),
281    /// dateTimeMilliseconds: milliseconds since the UNIX epoch
282    DateTimeMilliseconds(u64),
283    /// dateTimeMicroseconds: NTP timestamp
284    DateTimeMicroseconds(NtpTimestamp),
285    /// dateTimeNanoseconds: NTP timestamp
286    DateTimeNanoseconds(NtpTimestamp),
287    /// ipv4Address
288    Ipv4Address(Ipv4Addr),
289    /// ipv6Address
290    Ipv6Address(Ipv6Addr),
291    /// basicList (RFC 6313)
292    BasicList(BasicList),
293    /// subTemplateList (RFC 6313)
294    SubTemplateList(SubTemplateList),
295    /// subTemplateMultiList (RFC 6313)
296    SubTemplateMultiList(SubTemplateMultiList),
297    /// Undecoded value (see the type-level documentation)
298    Raw(Vec<u8>),
299}