Skip to main content

netgauze_flow_pkt/
lib.rs

1// Copyright (C) 2022-present The NetGauze Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12// implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Flow packet types and utilities for IPFIX and NetFlow v9.
17//!
18//! This crate provides data models, information elements, and (optional)
19//! codecs for working with IPFIX and NetFlow v9 packets. It includes helpers
20//! to inspect packet metadata and to manipulate fields for IPFIX data records.
21//!
22//! # Example
23//!
24//! ```rust
25//! use chrono::{TimeZone, Utc};
26//! use netgauze_flow_pkt::ipfix::{IpfixPacket, Set};
27//! use netgauze_flow_pkt::{DataSetId, FlowInfo};
28//!
29//! let export_time = Utc.with_ymd_and_hms(2024, 6, 20, 14, 0, 0).unwrap();
30//! let ipfix = IpfixPacket::new(
31//!     export_time,
32//!     1,
33//!     42,
34//!     Box::new([Set::Data {
35//!         id: DataSetId::new(400).unwrap(),
36//!         records: Box::new([]),
37//!     }]),
38//! );
39//!
40//! let flow = FlowInfo::IPFIX(ipfix);
41//! assert_eq!(flow.observation_domain_id(), 42);
42//! ```
43
44#[cfg(feature = "codec")]
45pub mod codec;
46pub mod ie;
47pub mod ipfix;
48pub mod netflow;
49#[cfg(feature = "serde")]
50pub mod wire;
51
52use crate::ie::*;
53use serde::{Deserialize, Serialize};
54use std::ops::Deref;
55use strum_macros::EnumDiscriminants;
56
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, EnumDiscriminants)]
58#[strum_discriminants(derive(Hash), name(FlowInfoType))]
59pub enum FlowInfo {
60    NetFlowV9(netflow::NetFlowV9Packet),
61    IPFIX(ipfix::IpfixPacket),
62}
63
64impl FlowInfo {
65    pub const fn export_time(&self) -> chrono::DateTime<chrono::Utc> {
66        match self {
67            Self::IPFIX(packet) => packet.export_time(),
68            Self::NetFlowV9(packet) => packet.unix_time(),
69        }
70    }
71
72    pub const fn sequence_number(&self) -> u32 {
73        match self {
74            Self::IPFIX(packet) => packet.sequence_number(),
75            Self::NetFlowV9(packet) => packet.sequence_number(),
76        }
77    }
78
79    pub const fn observation_domain_id(&self) -> u32 {
80        match self {
81            Self::IPFIX(packet) => packet.observation_domain_id(),
82            Self::NetFlowV9(packet) => packet.source_id(),
83        }
84    }
85
86    /// Returns an iterator over the fields of each data record in the packet,
87    /// yielding `(DataSetId, &[Field])` tuples — one per data record.
88    pub fn data_record_fields(&self) -> impl Iterator<Item = (DataSetId, &[Field])> {
89        match self {
90            Self::IPFIX(pkt) => either::Either::Left(
91                pkt.sets()
92                    .iter()
93                    .flat_map(|set| {
94                        if let ipfix::Set::Data { id, records } = set {
95                            Some(records.iter().map(move |record| (*id, record.fields())))
96                        } else {
97                            None
98                        }
99                    })
100                    .flatten(),
101            ),
102            Self::NetFlowV9(pkt) => either::Either::Right(
103                pkt.sets()
104                    .iter()
105                    .flat_map(|set| {
106                        if let netflow::Set::Data { id, records } = set {
107                            Some(records.iter().map(move |record| (*id, record.fields())))
108                        } else {
109                            None
110                        }
111                    })
112                    .flatten(),
113            ),
114        }
115    }
116}
117
118/// Errors when crafting a new Set
119#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, strum_macros::Display)]
120pub enum FieldSpecifierError {
121    /// Specified field length was out of the range defined by the registry
122    #[strum(to_string = "Invalid length specified {0} for IE: {1:?}")]
123    InvalidLength(u16, IE),
124}
125
126impl std::error::Error for FieldSpecifierError {}
127
128/// Field Specifier
129///
130/// ```text
131/// 0                   1                   2                   3
132/// 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
133/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
134/// |E|  Information Element ident. |        Field Length           |
135/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
136/// |                      Enterprise Number                        |
137/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138/// ```
139#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
140#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
141pub struct FieldSpecifier {
142    element_id: IE,
143    length: u16,
144}
145
146impl FieldSpecifier {
147    pub fn new(element_id: IE, length: u16) -> Result<Self, FieldSpecifierError> {
148        if let Some(range) = element_id.length_range()
149            && !range.contains(&length)
150        {
151            return Err(FieldSpecifierError::InvalidLength(length, element_id));
152        };
153        Ok(Self { element_id, length })
154    }
155
156    pub const fn element_id(&self) -> IE {
157        self.element_id
158    }
159
160    pub const fn length(&self) -> u16 {
161        self.length
162    }
163}
164
165#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, strum_macros::Display)]
166pub enum DataSetIdError {
167    #[strum(serialize = "Invalid data set id specified: {0}")]
168    InvalidId(u16),
169}
170
171impl std::error::Error for DataSetIdError {}
172
173#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
174#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
175pub struct DataSetId(u16);
176
177/// Values 256 and above are used for Data Sets
178pub(crate) const DATA_SET_MIN_ID: u16 = 256;
179
180impl DataSetId {
181    pub const fn new(id: u16) -> Result<Self, DataSetIdError> {
182        if id < DATA_SET_MIN_ID {
183            Err(DataSetIdError::InvalidId(id))
184        } else {
185            Ok(Self(id))
186        }
187    }
188
189    #[inline]
190    pub const fn id(&self) -> u16 {
191        self.0
192    }
193}
194
195impl Deref for DataSetId {
196    type Target = u16;
197
198    fn deref(&self) -> &Self::Target {
199        &self.0
200    }
201}
202
203#[cfg(feature = "fuzz")]
204fn arbitrary_datetime(
205    u: &mut arbitrary::Unstructured<'_>,
206) -> arbitrary::Result<chrono::DateTime<chrono::Utc>> {
207    use chrono::TimeZone;
208    loop {
209        let seconds = u.int_in_range(0..=i64::MAX)?;
210        if let chrono::LocalResult::Single(tt) = chrono::Utc.timestamp_opt(seconds, 0) {
211            return Ok(tt);
212        }
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219    use crate::ie::protocolIdentifier;
220    use crate::ipfix::IpfixPacket;
221    use crate::netflow::NetFlowV9Packet;
222    use chrono::{TimeZone, Utc};
223    use netgauze_iana::tcp::TCPHeaderFlags;
224
225    #[test]
226    fn test_flow_info_api() {
227        let export_time = Utc.with_ymd_and_hms(2024, 6, 20, 14, 0, 0).unwrap();
228        let sequence_number = 2;
229        let observation_domain = 100;
230        let ipfix_data = IpfixPacket::new(
231            export_time,
232            sequence_number,
233            observation_domain,
234            Box::new([ipfix::Set::Data {
235                id: DataSetId::new(400).unwrap(),
236                records: Box::new([]),
237            }]),
238        );
239        let netflow_data = NetFlowV9Packet::new(
240            45646,
241            export_time,
242            sequence_number,
243            observation_domain,
244            Box::new([netflow::Set::Data {
245                id: DataSetId::new(400).unwrap(),
246                records: Box::new([]),
247            }]),
248        );
249
250        let flow_ipfix = FlowInfo::IPFIX(ipfix_data.clone());
251        let flow_netflow = FlowInfo::NetFlowV9(netflow_data.clone());
252
253        assert_eq!(ipfix_data.export_time(), export_time);
254        assert_eq!(ipfix_data.sequence_number(), sequence_number);
255        assert_eq!(ipfix_data.observation_domain_id(), observation_domain);
256        assert_eq!(netflow_data.unix_time(), export_time);
257        assert_eq!(netflow_data.sequence_number(), sequence_number);
258        assert_eq!(netflow_data.source_id(), observation_domain);
259        assert_eq!(flow_ipfix.export_time(), export_time);
260        assert_eq!(flow_ipfix.sequence_number(), sequence_number);
261        assert_eq!(flow_ipfix.observation_domain_id(), observation_domain);
262        assert_eq!(flow_netflow.export_time(), export_time);
263        assert_eq!(flow_netflow.sequence_number(), sequence_number);
264        assert_eq!(flow_netflow.observation_domain_id(), observation_domain);
265    }
266
267    #[test]
268    fn test_supports_arithmetic() {
269        // octetArray doesn't support arithmetic operations
270        assert!(!IE::mplsLabelStackSection.supports_arithmetic_ops());
271        assert!(!IE::paddingOctets.supports_arithmetic_ops());
272        // number types (except unsigned256) supports arithmetic ops,
273        assert!(IE::destinationIPv4PrefixLength.supports_arithmetic_ops());
274        assert!(IE::flowActiveTimeout.supports_arithmetic_ops());
275        assert!(IE::distinctCountOfSourceIPv4Address.supports_arithmetic_ops());
276        assert!(IE::postMCastPacketDeltaCount.supports_arithmetic_ops());
277        assert!(!IE::ipv6ExtensionHeadersFull.supports_arithmetic_ops());
278        assert!(IE::mibObjectValueInteger.supports_arithmetic_ops());
279        assert!(IE::absoluteError.supports_arithmetic_ops());
280        // numbers that are identifiers, flags, or have subregistries don't support
281        // arithmetic ops
282        assert!(!IE::ipClassOfService.supports_arithmetic_ops());
283        assert!(!IE::egressInterface.supports_arithmetic_ops());
284        assert!(!IE::forwardingStatus.supports_arithmetic_ops());
285        // Bool doesn't support arithmetic ops
286        assert!(!IE::dataRecordsReliability.supports_arithmetic_ops());
287        // Time doesn't support arithmetic ops
288        assert!(!IE::observationTimeSeconds.supports_arithmetic_ops());
289        assert!(!IE::observationTimeMilliseconds.supports_arithmetic_ops());
290        assert!(!IE::observationTimeNanoseconds.supports_arithmetic_ops());
291        assert!(!IE::observationTimeMicroseconds.supports_arithmetic_ops());
292        // IP addresses don't support arithmetic ops
293        assert!(!IE::sourceIPv4Address.supports_arithmetic_ops());
294        assert!(!IE::sourceIPv6Address.supports_arithmetic_ops());
295        // List doesn't support arithmetic ops
296        assert!(!IE::bgpSourceCommunityList.supports_arithmetic_ops());
297        assert!(!IE::ipv6ExtensionHeaderTypeCountList.supports_arithmetic_ops());
298        assert!(!IE::subTemplateMultiList.supports_arithmetic_ops());
299    }
300
301    #[test]
302    fn test_supports_bitwise_ops() {
303        // octetArray supports bitwise operations
304        assert!(IE::mplsLabelStackSection.supports_bitwise_ops());
305        assert!(IE::paddingOctets.supports_bitwise_ops());
306        // number types (including unsigned256) supports bitwise ops,
307        assert!(IE::destinationIPv4PrefixLength.supports_bitwise_ops());
308        assert!(IE::flowActiveTimeout.supports_bitwise_ops());
309        assert!(IE::distinctCountOfSourceIPv4Address.supports_bitwise_ops());
310        assert!(IE::postMCastPacketDeltaCount.supports_bitwise_ops());
311        assert!(IE::ipv6ExtensionHeadersFull.supports_bitwise_ops());
312        assert!(IE::mibObjectValueInteger.supports_bitwise_ops());
313        // numbers that are identifiers, flags, or have subregistries support bitwise
314        // ops
315        assert!(IE::ipClassOfService.supports_bitwise_ops());
316        assert!(IE::egressInterface.supports_bitwise_ops());
317        assert!(IE::forwardingStatus.supports_bitwise_ops());
318        // Bool doesn't support bitwise ops
319        assert!(IE::dataRecordsReliability.supports_bitwise_ops());
320        // Time doesn't support bitwise ops
321        assert!(!IE::observationTimeSeconds.supports_bitwise_ops());
322        assert!(!IE::observationTimeMilliseconds.supports_bitwise_ops());
323        assert!(!IE::observationTimeNanoseconds.supports_bitwise_ops());
324        assert!(!IE::observationTimeMicroseconds.supports_bitwise_ops());
325        // IP addresses support bitwise ops
326        assert!(IE::sourceIPv4Address.supports_bitwise_ops());
327        assert!(IE::sourceIPv6Address.supports_bitwise_ops());
328    }
329
330    #[test]
331    fn test_supports_comparison_ops() {
332        // octetArray doesn't support comparison operations
333        assert!(!IE::mplsLabelStackSection.supports_comparison_ops());
334        assert!(!IE::paddingOctets.supports_comparison_ops());
335        // number types (excluding unsigned256) supports comparison ops,
336        assert!(IE::destinationIPv4PrefixLength.supports_comparison_ops());
337        assert!(IE::flowActiveTimeout.supports_comparison_ops());
338        assert!(IE::distinctCountOfSourceIPv4Address.supports_comparison_ops());
339        assert!(IE::postMCastPacketDeltaCount.supports_comparison_ops());
340        assert!(!IE::ipv6ExtensionHeadersFull.supports_comparison_ops());
341        assert!(IE::mibObjectValueInteger.supports_comparison_ops());
342        // numbers that are identifiers, flags, or have subregistries support comparison
343        // ops
344        assert!(IE::ipClassOfService.supports_comparison_ops());
345        assert!(IE::egressInterface.supports_comparison_ops());
346        assert!(IE::forwardingStatus.supports_comparison_ops());
347        // Bool doesn't support comparison ops
348        assert!(!IE::dataRecordsReliability.supports_comparison_ops());
349        // Time supports comparison ops
350        assert!(IE::observationTimeSeconds.supports_comparison_ops());
351        assert!(IE::observationTimeMilliseconds.supports_comparison_ops());
352        assert!(IE::observationTimeNanoseconds.supports_comparison_ops());
353        assert!(IE::observationTimeMicroseconds.supports_comparison_ops());
354        // IP addresses support comparison ops
355        assert!(IE::sourceIPv4Address.supports_comparison_ops());
356        assert!(IE::sourceIPv6Address.supports_comparison_ops());
357    }
358
359    #[test]
360    fn test_field_add() {
361        let mut octet1 = Field::octetDeltaCount(100);
362        let octet2 = Field::octetDeltaCount(200);
363        let packet1 = Field::packetDeltaCount(300);
364
365        let result_err1 = octet1.add_field(&packet1);
366        let result_err2 = octet1.add_assign_field(&packet1);
367        let result = octet1.add_field(&octet2).expect("add field");
368        octet1
369            .add_assign_field(&octet2)
370            .expect("add field mut failed");
371        let expected = Field::octetDeltaCount(300);
372        let expected_err = Some(FieldOperationError::InapplicableAdd(
373            IE::octetDeltaCount,
374            IE::packetDeltaCount,
375        ));
376
377        assert_eq!(result, expected);
378        assert_eq!(octet1, expected);
379        assert_eq!(result_err1.err(), expected_err);
380        assert_eq!(result_err2.err(), expected_err);
381    }
382
383    #[test]
384    fn test_field_min() {
385        let mut field1 = Field::octetDeltaCount(100);
386        let field2 = Field::octetDeltaCount(200);
387        let packet1 = Field::packetDeltaCount(300);
388
389        let result_err1 = field1.min_field(&packet1);
390        let result_err2 = field1.min_assign_field(&packet1);
391        let result = field1.min_field(&field2).expect("min field");
392        field1
393            .min_assign_field(&field2)
394            .expect("min field mut failed");
395        let expected = Field::octetDeltaCount(100);
396        let expected_err = Some(FieldOperationError::InapplicableMin(
397            IE::octetDeltaCount,
398            IE::packetDeltaCount,
399        ));
400
401        assert_eq!(result, expected);
402        assert_eq!(field1, expected);
403        assert_eq!(result_err1.err(), expected_err);
404        assert_eq!(result_err2.err(), expected_err);
405    }
406
407    #[test]
408    fn test_field_max() {
409        let mut field1 = Field::octetDeltaCount(100);
410        let field2 = Field::octetDeltaCount(200);
411        let packet1 = Field::packetDeltaCount(300);
412
413        let result_err1 = field1.max_field(&packet1);
414        let result_err2 = field1.max_assign_field(&packet1);
415        let result = field1.max_field(&field2).expect("max field");
416        field1
417            .max_assign_field(&field2)
418            .expect("max field mut failed");
419        let expected = Field::octetDeltaCount(200);
420        let expected_err = Some(FieldOperationError::InapplicableMax(
421            IE::octetDeltaCount,
422            IE::packetDeltaCount,
423        ));
424
425        assert_eq!(result, expected);
426        assert_eq!(field1, expected);
427        assert_eq!(result_err1.err(), expected_err);
428        assert_eq!(result_err2.err(), expected_err);
429    }
430
431    #[test]
432    fn test_field_bitwise_or() {
433        let mut field1 = Field::octetDeltaCount(100);
434        let field2 = Field::octetDeltaCount(200);
435        let packet1 = Field::packetDeltaCount(300);
436
437        let result_err1 = field1.bitwise_or_field(&packet1);
438        let result_err2 = field1.bitwise_or_assign_field(&packet1);
439        let result = field1.bitwise_or_field(&field2).expect("bitwise or field");
440        field1
441            .bitwise_or_assign_field(&field2)
442            .expect("bitwise or field mut failed");
443        let expected = Field::octetDeltaCount(236);
444        let expected_err = Some(FieldOperationError::InapplicableBitwise(
445            IE::octetDeltaCount,
446            IE::packetDeltaCount,
447        ));
448
449        assert_eq!(result, expected);
450        assert_eq!(field1, expected);
451        assert_eq!(result_err1.err(), expected_err);
452        assert_eq!(result_err2.err(), expected_err);
453    }
454
455    #[test]
456    fn test_field_bitwise_or_tcp_control_bits() {
457        let mut field1 = Field::tcpControlBits(TCPHeaderFlags::from(0x01u8));
458        let field2 = Field::tcpControlBits(TCPHeaderFlags::from(0x02u8));
459        let packet1 = Field::packetDeltaCount(300);
460
461        let result_err1 = field1.bitwise_or_field(&packet1);
462        let result_err2 = field1.bitwise_or_assign_field(&packet1);
463        let result = field1.bitwise_or_field(&field2).expect("bitwise or field");
464        field1
465            .bitwise_or_assign_field(&field2)
466            .expect("bitwise or field mut failed");
467        let expected = Field::tcpControlBits(TCPHeaderFlags::from(0x03u8));
468        let expected_err = Some(FieldOperationError::InapplicableBitwise(
469            IE::tcpControlBits,
470            IE::packetDeltaCount,
471        ));
472
473        assert_eq!(result, expected);
474        assert_eq!(field1, expected);
475        assert_eq!(result_err1.err(), expected_err);
476        assert_eq!(result_err2.err(), expected_err);
477    }
478
479    #[test]
480    fn test_field_bitwise_or_protocol_identifier() {
481        let mut field1 = Field::protocolIdentifier(protocolIdentifier::ICMP);
482        let field2 = Field::protocolIdentifier(protocolIdentifier::IGMP);
483        let packet1 = Field::packetDeltaCount(300);
484
485        let result_err1 = field1.bitwise_or_field(&packet1);
486        let result_err2 = field1.bitwise_or_assign_field(&packet1);
487        let result = field1.bitwise_or_field(&field2).expect("bitwise or field");
488        field1
489            .bitwise_or_assign_field(&field2)
490            .expect("bitwise or field mut failed");
491        let expected = Field::protocolIdentifier(protocolIdentifier::from(0x03u8));
492        let expected_err = Some(FieldOperationError::InapplicableBitwise(
493            IE::protocolIdentifier,
494            IE::packetDeltaCount,
495        ));
496
497        assert_eq!(result, expected);
498        assert_eq!(field1, expected);
499        assert_eq!(result_err1.err(), expected_err);
500        assert_eq!(result_err2.err(), expected_err);
501    }
502
503    #[test]
504    fn test_vmware_ops() {
505        let mut vendor_field1 = vmware::Field::averageLatency(100);
506        let vendor_field2 = vmware::Field::averageLatency(200);
507        let vendor_other_field = vmware::Field::algControlFlowId(123);
508
509        let result_err1 = vendor_field1.add_field(&vendor_other_field);
510        let result_err2 = vendor_field1.add_assign_field(&vendor_other_field);
511        let result = vendor_field1.add_field(&vendor_field2).expect("add field");
512        vendor_field1
513            .add_assign_field(&vendor_field2)
514            .expect("add field");
515        let expected = vmware::Field::averageLatency(300);
516        let expected_err = Some(vmware::FieldOperationError::InapplicableAdd(
517            vmware::IE::averageLatency,
518            vmware::IE::algControlFlowId,
519        ));
520
521        assert_eq!(result, expected);
522        assert_eq!(vendor_field1, expected);
523        assert_eq!(result_err1.err(), expected_err);
524        assert_eq!(result_err2.err(), expected_err);
525    }
526
527    #[test]
528    fn test_vendor_field_add() {
529        let mut field1 = Field::VMWare(vmware::Field::averageLatency(100));
530        let field2 = Field::VMWare(vmware::Field::averageLatency(200));
531        let other_field = Field::VMWare(vmware::Field::algControlFlowId(123));
532
533        let result_err1 = field1.add_field(&other_field);
534        let result_err2 = field1.add_assign_field(&other_field);
535        let result = field1.add_field(&field2).expect("add field");
536        field1.add_assign_field(&field2).expect("add field");
537        let expected = Field::VMWare(vmware::Field::averageLatency(300));
538        let expected_err = Some(FieldOperationError::InapplicableAdd(
539            IE::VMWare(vmware::IE::averageLatency),
540            IE::VMWare(vmware::IE::algControlFlowId),
541        ));
542
543        assert_eq!(result, expected);
544        assert_eq!(field1, expected);
545        assert_eq!(result_err1.err(), expected_err);
546        assert_eq!(result_err2.err(), expected_err);
547    }
548
549    #[test]
550    fn test_vendor_field_min() {
551        let mut field1 = Field::VMWare(vmware::Field::averageLatency(100));
552        let field2 = Field::VMWare(vmware::Field::averageLatency(200));
553        let other_field = Field::VMWare(vmware::Field::algControlFlowId(123));
554
555        let result_err1 = field1.min_field(&other_field);
556        let result_err2 = field1.min_assign_field(&other_field);
557        let result = field1.min_field(&field2).expect("min field");
558        field1.min_assign_field(&field2).expect("min field");
559        let expected = Field::VMWare(vmware::Field::averageLatency(100));
560        let expected_err = Some(FieldOperationError::InapplicableMin(
561            IE::VMWare(vmware::IE::averageLatency),
562            IE::VMWare(vmware::IE::algControlFlowId),
563        ));
564
565        assert_eq!(result, expected);
566        assert_eq!(field1, expected);
567        assert_eq!(result_err1.err(), expected_err);
568        assert_eq!(result_err2.err(), expected_err);
569    }
570
571    #[test]
572    fn test_vendor_field_max() {
573        let mut field1 = Field::VMWare(vmware::Field::averageLatency(100));
574        let field2 = Field::VMWare(vmware::Field::averageLatency(200));
575        let other_field = Field::VMWare(vmware::Field::algControlFlowId(123));
576
577        let result_err1 = field1.max_field(&other_field);
578        let result_err2 = field1.max_assign_field(&other_field);
579        let result = field1.max_field(&field2).expect("max field");
580        field1.max_assign_field(&field2).expect("max field");
581        let expected = Field::VMWare(vmware::Field::averageLatency(200));
582        let expected_err = Some(FieldOperationError::InapplicableMax(
583            IE::VMWare(vmware::IE::averageLatency),
584            IE::VMWare(vmware::IE::algControlFlowId),
585        ));
586
587        assert_eq!(result, expected);
588        assert_eq!(field1, expected);
589        assert_eq!(result_err1.err(), expected_err);
590        assert_eq!(result_err2.err(), expected_err);
591    }
592
593    #[test]
594    fn test_vendor_field_bitwise_or() {
595        let mut field1 = Field::VMWare(vmware::Field::algControlFlowId(100));
596        let field2 = Field::VMWare(vmware::Field::algControlFlowId(200));
597        let other_field = Field::VMWare(vmware::Field::averageLatency(123));
598
599        let result_err1 = field1.bitwise_or_field(&other_field);
600        let result_err2 = field1.bitwise_or_field(&other_field);
601        let result = field1.bitwise_or_field(&field2).expect("bitwise or field");
602        field1
603            .bitwise_or_assign_field(&field2)
604            .expect("bitwise or field");
605        let expected = Field::VMWare(vmware::Field::algControlFlowId(236));
606        let expected_err = Some(FieldOperationError::InapplicableBitwise(
607            IE::VMWare(vmware::IE::algControlFlowId),
608            IE::VMWare(vmware::IE::averageLatency),
609        ));
610
611        assert_eq!(result, expected);
612        assert_eq!(field1, expected);
613        assert_eq!(result_err1.err(), expected_err);
614        assert_eq!(result_err2.err(), expected_err);
615    }
616}