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}