1use std::net::IpAddr;
2
3use crate::NetflowPacket;
4use crate::protocol::ProtocolTypes;
5use crate::static_versions::{v5::V5, v7::V7};
6use crate::variable_versions::data_number::FieldValue;
7use crate::variable_versions::ipfix_lookup::{IANAIPFixField, IPFixField};
8use crate::variable_versions::v9_lookup::V9Field;
9use crate::variable_versions::{
10 ipfix::{FlowSetBody as IPFixFlowSetBody, IPFix},
11 v9::{FlowSetBody as V9FlowSetBody, V9},
12};
13
14#[derive(Debug)]
15pub enum NetflowCommonError {
16 UnknownVersion(NetflowPacket),
17}
18
19#[derive(Debug, Clone)]
22pub struct FieldMapping<T> {
23 pub primary: T,
25 pub fallback: Option<T>,
27}
28
29impl<T> FieldMapping<T> {
30 pub fn new(primary: T) -> Self {
32 Self {
33 primary,
34 fallback: None,
35 }
36 }
37
38 pub fn with_fallback(primary: T, fallback: T) -> Self {
40 Self {
41 primary,
42 fallback: Some(fallback),
43 }
44 }
45}
46
47pub type V9FieldMapping = FieldMapping<V9Field>;
49
50pub type IPFixFieldMapping = FieldMapping<IPFixField>;
52
53#[derive(Debug, Clone)]
73pub struct V9FieldMappingConfig {
74 pub src_addr: V9FieldMapping,
76 pub dst_addr: V9FieldMapping,
78 pub src_port: V9FieldMapping,
80 pub dst_port: V9FieldMapping,
82 pub protocol: V9FieldMapping,
84 pub first_seen: V9FieldMapping,
86 pub last_seen: V9FieldMapping,
88 pub src_mac: V9FieldMapping,
90 pub dst_mac: V9FieldMapping,
92}
93
94impl Default for V9FieldMappingConfig {
95 fn default() -> Self {
96 Self {
97 src_addr: V9FieldMapping::with_fallback(V9Field::Ipv4SrcAddr, V9Field::Ipv6SrcAddr),
98 dst_addr: V9FieldMapping::with_fallback(V9Field::Ipv4DstAddr, V9Field::Ipv6DstAddr),
99 src_port: V9FieldMapping::new(V9Field::L4SrcPort),
100 dst_port: V9FieldMapping::new(V9Field::L4DstPort),
101 protocol: V9FieldMapping::new(V9Field::Protocol),
102 first_seen: V9FieldMapping::new(V9Field::FirstSwitched),
103 last_seen: V9FieldMapping::new(V9Field::LastSwitched),
104 src_mac: V9FieldMapping::new(V9Field::InSrcMac),
105 dst_mac: V9FieldMapping::new(V9Field::InDstMac),
106 }
107 }
108}
109
110#[derive(Debug, Clone)]
130pub struct IPFixFieldMappingConfig {
131 pub src_addr: IPFixFieldMapping,
133 pub dst_addr: IPFixFieldMapping,
135 pub src_port: IPFixFieldMapping,
137 pub dst_port: IPFixFieldMapping,
139 pub protocol: IPFixFieldMapping,
141 pub first_seen: IPFixFieldMapping,
143 pub last_seen: IPFixFieldMapping,
145 pub src_mac: IPFixFieldMapping,
147 pub dst_mac: IPFixFieldMapping,
149}
150
151impl Default for IPFixFieldMappingConfig {
152 fn default() -> Self {
153 Self {
154 src_addr: IPFixFieldMapping::with_fallback(
155 IPFixField::IANA(IANAIPFixField::SourceIpv4address),
156 IPFixField::IANA(IANAIPFixField::SourceIpv6address),
157 ),
158 dst_addr: IPFixFieldMapping::with_fallback(
159 IPFixField::IANA(IANAIPFixField::DestinationIpv4address),
160 IPFixField::IANA(IANAIPFixField::DestinationIpv6address),
161 ),
162 src_port: IPFixFieldMapping::new(IPFixField::IANA(
163 IANAIPFixField::SourceTransportPort,
164 )),
165 dst_port: IPFixFieldMapping::new(IPFixField::IANA(
166 IANAIPFixField::DestinationTransportPort,
167 )),
168 protocol: IPFixFieldMapping::new(IPFixField::IANA(
169 IANAIPFixField::ProtocolIdentifier,
170 )),
171 first_seen: IPFixFieldMapping::new(IPFixField::IANA(
172 IANAIPFixField::FlowStartSysUpTime,
173 )),
174 last_seen: IPFixFieldMapping::new(IPFixField::IANA(
175 IANAIPFixField::FlowEndSysUpTime,
176 )),
177 src_mac: IPFixFieldMapping::new(IPFixField::IANA(IANAIPFixField::SourceMacaddress)),
178 dst_mac: IPFixFieldMapping::new(IPFixField::IANA(
179 IANAIPFixField::DestinationMacaddress,
180 )),
181 }
182 }
183}
184
185#[derive(Debug, Default)]
186pub struct NetflowCommon {
188 pub version: u16,
189 pub timestamp: u32,
190 pub flowsets: Vec<NetflowCommonFlowSet>,
191}
192
193impl TryFrom<&NetflowPacket> for NetflowCommon {
194 type Error = NetflowCommonError;
195
196 fn try_from(value: &NetflowPacket) -> Result<Self, NetflowCommonError> {
197 match value {
198 NetflowPacket::V5(v5) => Ok(v5.into()),
199 NetflowPacket::V7(v7) => Ok(v7.into()),
200 NetflowPacket::V9(v9) => Ok(v9.into()),
201 NetflowPacket::IPFix(ipfix) => Ok(ipfix.into()),
202 }
203 }
204}
205
206#[derive(Debug, Default)]
207pub struct NetflowCommonFlowSet {
209 pub src_addr: Option<IpAddr>,
211 pub dst_addr: Option<IpAddr>,
213 pub src_port: Option<u16>,
215 pub dst_port: Option<u16>,
217 pub protocol_number: Option<u8>,
219 pub protocol_type: Option<ProtocolTypes>,
221 pub first_seen: Option<u32>,
223 pub last_seen: Option<u32>,
225 pub src_mac: Option<String>,
227 pub dst_mac: Option<String>,
229}
230
231impl From<&V5> for NetflowCommon {
232 fn from(value: &V5) -> Self {
233 NetflowCommon {
235 version: value.header.version,
236 timestamp: value.header.sys_up_time,
237 flowsets: value
238 .flowsets
239 .iter()
240 .map(|set| NetflowCommonFlowSet {
241 src_addr: Some(set.src_addr.into()),
242 dst_addr: Some(set.dst_addr.into()),
243 src_port: Some(set.src_port),
244 dst_port: Some(set.dst_port),
245 protocol_number: Some(set.protocol_number),
246 protocol_type: Some(set.protocol_type),
247 first_seen: Some(set.first),
248 last_seen: Some(set.last),
249 src_mac: None,
250 dst_mac: None,
251 })
252 .collect(),
253 }
254 }
255}
256
257impl From<&V7> for NetflowCommon {
258 fn from(value: &V7) -> Self {
259 NetflowCommon {
261 version: value.header.version,
262 timestamp: value.header.sys_up_time,
263 flowsets: value
264 .flowsets
265 .iter()
266 .map(|set| NetflowCommonFlowSet {
267 src_addr: Some(set.src_addr.into()),
268 dst_addr: Some(set.dst_addr.into()),
269 src_port: Some(set.src_port),
270 dst_port: Some(set.dst_port),
271 protocol_number: Some(set.protocol_number),
272 protocol_type: Some(set.protocol_type),
273 first_seen: Some(set.first),
274 last_seen: Some(set.last),
275 src_mac: None,
276 dst_mac: None,
277 })
278 .collect(),
279 }
280 }
281}
282
283#[derive(Copy, Clone)]
285struct V9FieldCache<'a> {
286 src_addr_v4: Option<&'a FieldValue>,
287 src_addr_v6: Option<&'a FieldValue>,
288 dst_addr_v4: Option<&'a FieldValue>,
289 dst_addr_v6: Option<&'a FieldValue>,
290 src_port: Option<&'a FieldValue>,
291 dst_port: Option<&'a FieldValue>,
292 protocol: Option<&'a FieldValue>,
293 first_seen: Option<&'a FieldValue>,
294 last_seen: Option<&'a FieldValue>,
295 src_mac: Option<&'a FieldValue>,
296 dst_mac: Option<&'a FieldValue>,
297}
298
299impl<'a> V9FieldCache<'a> {
300 fn from_fields(fields: &'a [(V9Field, FieldValue)]) -> Self {
301 let mut cache = Self {
302 src_addr_v4: None,
303 src_addr_v6: None,
304 dst_addr_v4: None,
305 dst_addr_v6: None,
306 src_port: None,
307 dst_port: None,
308 protocol: None,
309 first_seen: None,
310 last_seen: None,
311 src_mac: None,
312 dst_mac: None,
313 };
314
315 for (field_type, field_value) in fields {
317 match field_type {
318 V9Field::Ipv4SrcAddr => cache.src_addr_v4 = Some(field_value),
319 V9Field::Ipv6SrcAddr => cache.src_addr_v6 = Some(field_value),
320 V9Field::Ipv4DstAddr => cache.dst_addr_v4 = Some(field_value),
321 V9Field::Ipv6DstAddr => cache.dst_addr_v6 = Some(field_value),
322 V9Field::L4SrcPort => cache.src_port = Some(field_value),
323 V9Field::L4DstPort => cache.dst_port = Some(field_value),
324 V9Field::Protocol => cache.protocol = Some(field_value),
325 V9Field::FirstSwitched => cache.first_seen = Some(field_value),
326 V9Field::LastSwitched => cache.last_seen = Some(field_value),
327 V9Field::InSrcMac => cache.src_mac = Some(field_value),
328 V9Field::InDstMac => cache.dst_mac = Some(field_value),
329 _ => {} }
331 }
332
333 cache
334 }
335}
336
337macro_rules! create_common_flowset_with_ip_versions {
339 ($cache:expr, $src_v4:ident, $src_v6:ident, $dst_v4:ident, $dst_v6:ident) => {
340 NetflowCommonFlowSet {
341 src_addr: $cache
342 .$src_v4
343 .or($cache.$src_v6)
344 .and_then(|v| v.try_into().ok()),
345 dst_addr: $cache
346 .$dst_v4
347 .or($cache.$dst_v6)
348 .and_then(|v| v.try_into().ok()),
349 src_port: $cache.src_port.and_then(|v| v.try_into().ok()),
350 dst_port: $cache.dst_port.and_then(|v| v.try_into().ok()),
351 protocol_number: $cache.protocol.and_then(|v| v.try_into().ok()),
352 protocol_type: $cache.protocol.and_then(|v| {
353 v.try_into()
354 .ok()
355 .map(|proto: u8| ProtocolTypes::from(proto))
356 }),
357 first_seen: $cache.first_seen.and_then(|v| v.try_into().ok()),
358 last_seen: $cache.last_seen.and_then(|v| v.try_into().ok()),
359 src_mac: $cache.src_mac.and_then(|v| v.try_into().ok()),
360 dst_mac: $cache.dst_mac.and_then(|v| v.try_into().ok()),
361 }
362 };
363}
364
365macro_rules! create_common_flowset {
367 ($cache:expr) => {
368 NetflowCommonFlowSet {
369 src_addr: $cache.src_addr.and_then(|v| v.try_into().ok()),
370 dst_addr: $cache.dst_addr.and_then(|v| v.try_into().ok()),
371 src_port: $cache.src_port.and_then(|v| v.try_into().ok()),
372 dst_port: $cache.dst_port.and_then(|v| v.try_into().ok()),
373 protocol_number: $cache.protocol.and_then(|v| v.try_into().ok()),
374 protocol_type: $cache.protocol.and_then(|v| {
375 v.try_into()
376 .ok()
377 .map(|proto: u8| ProtocolTypes::from(proto))
378 }),
379 first_seen: $cache.first_seen.and_then(|v| v.try_into().ok()),
380 last_seen: $cache.last_seen.and_then(|v| v.try_into().ok()),
381 src_mac: $cache.src_mac.and_then(|v| v.try_into().ok()),
382 dst_mac: $cache.dst_mac.and_then(|v| v.try_into().ok()),
383 }
384 };
385}
386
387macro_rules! check_field_mapping {
389 ($field_type:expr, $field_value:expr, $cache:expr, $config:expr, $field_name:ident) => {
390 if *$field_type == $config.$field_name.primary {
391 $cache.$field_name = Some($field_value);
392 } else if Some(*$field_type) == $config.$field_name.fallback
393 && $cache.$field_name.is_none()
394 {
395 $cache.$field_name = Some($field_value);
396 }
397 };
398}
399
400#[derive(Copy, Clone)]
402struct V9ConfigFieldCache<'a> {
403 src_addr: Option<&'a FieldValue>,
404 dst_addr: Option<&'a FieldValue>,
405 src_port: Option<&'a FieldValue>,
406 dst_port: Option<&'a FieldValue>,
407 protocol: Option<&'a FieldValue>,
408 first_seen: Option<&'a FieldValue>,
409 last_seen: Option<&'a FieldValue>,
410 src_mac: Option<&'a FieldValue>,
411 dst_mac: Option<&'a FieldValue>,
412}
413
414impl<'a> V9ConfigFieldCache<'a> {
415 fn from_fields_with_config(
416 fields: &'a [(V9Field, FieldValue)],
417 config: &V9FieldMappingConfig,
418 ) -> Self {
419 let mut cache = Self {
420 src_addr: None,
421 dst_addr: None,
422 src_port: None,
423 dst_port: None,
424 protocol: None,
425 first_seen: None,
426 last_seen: None,
427 src_mac: None,
428 dst_mac: None,
429 };
430
431 for (field_type, field_value) in fields {
433 check_field_mapping!(field_type, field_value, cache, config, src_addr);
434 check_field_mapping!(field_type, field_value, cache, config, dst_addr);
435 check_field_mapping!(field_type, field_value, cache, config, src_port);
436 check_field_mapping!(field_type, field_value, cache, config, dst_port);
437 check_field_mapping!(field_type, field_value, cache, config, protocol);
438 check_field_mapping!(field_type, field_value, cache, config, first_seen);
439 check_field_mapping!(field_type, field_value, cache, config, last_seen);
440 check_field_mapping!(field_type, field_value, cache, config, src_mac);
441 check_field_mapping!(field_type, field_value, cache, config, dst_mac);
442 }
443
444 cache
445 }
446}
447
448impl NetflowCommon {
449 pub fn from_v9_with_config(value: &V9, config: &V9FieldMappingConfig) -> Self {
468 let mut flowsets = vec![];
469
470 for flowset in &value.flowsets {
471 if let V9FlowSetBody::Data(data) = &flowset.body {
472 for data_field in &data.fields {
473 let cache = V9ConfigFieldCache::from_fields_with_config(data_field, config);
475 flowsets.push(create_common_flowset!(cache));
476 }
477 }
478 }
479
480 NetflowCommon {
481 version: value.header.version,
482 timestamp: value.header.sys_up_time,
483 flowsets,
484 }
485 }
486}
487
488impl From<&V9> for NetflowCommon {
489 fn from(value: &V9) -> Self {
490 let mut flowsets = vec![];
492
493 for flowset in &value.flowsets {
494 if let V9FlowSetBody::Data(data) = &flowset.body {
495 for data_field in &data.fields {
496 let cache = V9FieldCache::from_fields(data_field);
498 flowsets.push(create_common_flowset_with_ip_versions!(
499 cache,
500 src_addr_v4,
501 src_addr_v6,
502 dst_addr_v4,
503 dst_addr_v6
504 ));
505 }
506 }
507 }
508
509 NetflowCommon {
510 version: value.header.version,
511 timestamp: value.header.sys_up_time,
512 flowsets,
513 }
514 }
515}
516
517#[derive(Copy, Clone)]
519struct IPFixFieldCache<'a> {
520 src_addr_v4: Option<&'a FieldValue>,
521 src_addr_v6: Option<&'a FieldValue>,
522 dst_addr_v4: Option<&'a FieldValue>,
523 dst_addr_v6: Option<&'a FieldValue>,
524 src_port: Option<&'a FieldValue>,
525 dst_port: Option<&'a FieldValue>,
526 protocol: Option<&'a FieldValue>,
527 first_seen: Option<&'a FieldValue>,
528 last_seen: Option<&'a FieldValue>,
529 src_mac: Option<&'a FieldValue>,
530 dst_mac: Option<&'a FieldValue>,
531}
532
533impl<'a> IPFixFieldCache<'a> {
534 fn from_fields(fields: &'a [(IPFixField, FieldValue)]) -> Self {
535 let mut cache = Self {
536 src_addr_v4: None,
537 src_addr_v6: None,
538 dst_addr_v4: None,
539 dst_addr_v6: None,
540 src_port: None,
541 dst_port: None,
542 protocol: None,
543 first_seen: None,
544 last_seen: None,
545 src_mac: None,
546 dst_mac: None,
547 };
548
549 for (field_type, field_value) in fields {
551 match field_type {
552 IPFixField::IANA(IANAIPFixField::SourceIpv4address) => {
553 cache.src_addr_v4 = Some(field_value)
554 }
555 IPFixField::IANA(IANAIPFixField::SourceIpv6address) => {
556 cache.src_addr_v6 = Some(field_value)
557 }
558 IPFixField::IANA(IANAIPFixField::DestinationIpv4address) => {
559 cache.dst_addr_v4 = Some(field_value)
560 }
561 IPFixField::IANA(IANAIPFixField::DestinationIpv6address) => {
562 cache.dst_addr_v6 = Some(field_value)
563 }
564 IPFixField::IANA(IANAIPFixField::SourceTransportPort) => {
565 cache.src_port = Some(field_value)
566 }
567 IPFixField::IANA(IANAIPFixField::DestinationTransportPort) => {
568 cache.dst_port = Some(field_value)
569 }
570 IPFixField::IANA(IANAIPFixField::ProtocolIdentifier) => {
571 cache.protocol = Some(field_value)
572 }
573 IPFixField::IANA(IANAIPFixField::FlowStartSysUpTime) => {
574 cache.first_seen = Some(field_value)
575 }
576 IPFixField::IANA(IANAIPFixField::FlowEndSysUpTime) => {
577 cache.last_seen = Some(field_value)
578 }
579 IPFixField::IANA(IANAIPFixField::SourceMacaddress) => {
580 cache.src_mac = Some(field_value)
581 }
582 IPFixField::IANA(IANAIPFixField::DestinationMacaddress) => {
583 cache.dst_mac = Some(field_value)
584 }
585 _ => {} }
587 }
588
589 cache
590 }
591}
592
593macro_rules! check_field_mapping_ref {
595 ($field_type:expr, $field_value:expr, $cache:expr, $config:expr, $field_name:ident) => {
596 if *$field_type == $config.$field_name.primary {
597 $cache.$field_name = Some($field_value);
598 } else if $config.$field_name.fallback.as_ref() == Some($field_type)
599 && $cache.$field_name.is_none()
600 {
601 $cache.$field_name = Some($field_value);
602 }
603 };
604}
605
606#[derive(Copy, Clone)]
608struct IPFixConfigFieldCache<'a> {
609 src_addr: Option<&'a FieldValue>,
610 dst_addr: Option<&'a FieldValue>,
611 src_port: Option<&'a FieldValue>,
612 dst_port: Option<&'a FieldValue>,
613 protocol: Option<&'a FieldValue>,
614 first_seen: Option<&'a FieldValue>,
615 last_seen: Option<&'a FieldValue>,
616 src_mac: Option<&'a FieldValue>,
617 dst_mac: Option<&'a FieldValue>,
618}
619
620impl<'a> IPFixConfigFieldCache<'a> {
621 fn from_fields_with_config(
622 fields: &'a [(IPFixField, FieldValue)],
623 config: &IPFixFieldMappingConfig,
624 ) -> Self {
625 let mut cache = Self {
626 src_addr: None,
627 dst_addr: None,
628 src_port: None,
629 dst_port: None,
630 protocol: None,
631 first_seen: None,
632 last_seen: None,
633 src_mac: None,
634 dst_mac: None,
635 };
636
637 for (field_type, field_value) in fields {
639 check_field_mapping_ref!(field_type, field_value, cache, config, src_addr);
640 check_field_mapping_ref!(field_type, field_value, cache, config, dst_addr);
641 check_field_mapping_ref!(field_type, field_value, cache, config, src_port);
642 check_field_mapping_ref!(field_type, field_value, cache, config, dst_port);
643 check_field_mapping_ref!(field_type, field_value, cache, config, protocol);
644 check_field_mapping_ref!(field_type, field_value, cache, config, first_seen);
645 check_field_mapping_ref!(field_type, field_value, cache, config, last_seen);
646 check_field_mapping_ref!(field_type, field_value, cache, config, src_mac);
647 check_field_mapping_ref!(field_type, field_value, cache, config, dst_mac);
648 }
649
650 cache
651 }
652}
653
654impl NetflowCommon {
655 pub fn from_ipfix_with_config(value: &IPFix, config: &IPFixFieldMappingConfig) -> Self {
674 let mut flowsets = vec![];
675
676 for flowset in &value.flowsets {
677 if let IPFixFlowSetBody::Data(data) = &flowset.body {
678 for data_field in &data.fields {
679 let cache =
681 IPFixConfigFieldCache::from_fields_with_config(data_field, config);
682 flowsets.push(create_common_flowset!(cache));
683 }
684 }
685 }
686
687 NetflowCommon {
688 version: value.header.version,
689 timestamp: value.header.export_time,
690 flowsets,
691 }
692 }
693}
694
695impl From<&IPFix> for NetflowCommon {
696 fn from(value: &IPFix) -> Self {
697 let mut flowsets = vec![];
699
700 for flowset in &value.flowsets {
701 if let IPFixFlowSetBody::Data(data) = &flowset.body {
702 for data_field in &data.fields {
703 let cache = IPFixFieldCache::from_fields(data_field);
705 flowsets.push(create_common_flowset_with_ip_versions!(
706 cache,
707 src_addr_v4,
708 src_addr_v6,
709 dst_addr_v4,
710 dst_addr_v6
711 ));
712 }
713 }
714 }
715
716 NetflowCommon {
717 version: value.header.version,
718 timestamp: value.header.export_time,
719 flowsets,
720 }
721 }
722}
723
724#[cfg(test)]
725mod common_tests {
726 use std::net::{IpAddr, Ipv4Addr};
727
728 use crate::netflow_common::NetflowCommon;
729 use crate::static_versions::v5::{FlowSet as V5FlowSet, Header as V5Header, V5};
730 use crate::static_versions::v7::{FlowSet as V7FlowSet, Header as V7Header, V7};
731 use crate::variable_versions::data_number::{DataNumber, FieldValue};
732 use crate::variable_versions::ipfix::{
733 Data as IPFixData, FlowSet as IPFixFlowSet, FlowSetBody as IPFixFlowSetBody,
734 FlowSetHeader as IPFixFlowSetHeader, Header as IPFixHeader, IPFix,
735 };
736 use crate::variable_versions::ipfix_lookup::{IANAIPFixField, IPFixField};
737 use crate::variable_versions::v9::{
738 Data as V9Data, FlowSet as V9FlowSet, FlowSetBody as V9FlowSetBody,
739 FlowSetHeader as V9FlowSetHeader, Header as V9Header, V9,
740 };
741 use crate::variable_versions::v9_lookup::V9Field;
742
743 #[test]
744 fn it_converts_v5_to_common() {
745 let v5 = V5 {
746 header: V5Header {
747 version: 5,
748 count: 1,
749 sys_up_time: 100,
750 unix_secs: 1609459200,
751 unix_nsecs: 0,
752 flow_sequence: 1,
753 engine_type: 0,
754 engine_id: 0,
755 sampling_interval: 0,
756 },
757 flowsets: vec![V5FlowSet {
758 src_addr: Ipv4Addr::new(192, 168, 1, 1),
759 dst_addr: Ipv4Addr::new(192, 168, 1, 2),
760 src_port: 1234,
761 dst_port: 80,
762 protocol_number: 6,
763 protocol_type: crate::protocol::ProtocolTypes::Tcp,
764 next_hop: Ipv4Addr::new(192, 168, 1, 254),
765 input: 0,
766 output: 0,
767 d_pkts: 10,
768 d_octets: 1000,
769 first: 100,
770 last: 200,
771 pad1: 0,
772 tcp_flags: 0,
773 tos: 0,
774 src_as: 0,
775 dst_as: 0,
776 src_mask: 0,
777 dst_mask: 0,
778 pad2: 0,
779 }],
780 };
781
782 let common: NetflowCommon = NetflowCommon::from(&v5);
783
784 assert_eq!(common.version, 5);
785 assert_eq!(common.timestamp, 100);
786 assert_eq!(common.flowsets.len(), 1);
787 let flowset = &common.flowsets[0];
788 assert_eq!(
789 flowset.src_addr.unwrap(),
790 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))
791 );
792 assert_eq!(
793 flowset.dst_addr.unwrap(),
794 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2))
795 );
796 assert_eq!(flowset.src_port.unwrap(), 1234);
797 assert_eq!(flowset.dst_port.unwrap(), 80);
798 assert_eq!(flowset.protocol_number.unwrap(), 6);
799 assert_eq!(
800 flowset.protocol_type.unwrap(),
801 crate::protocol::ProtocolTypes::Tcp
802 );
803 assert_eq!(flowset.first_seen.unwrap(), 100);
804 assert_eq!(flowset.last_seen.unwrap(), 200);
805 }
806
807 #[test]
808 fn it_converts_v7_to_common() {
809 let v7 = V7 {
810 header: V7Header {
811 version: 7,
812 count: 1,
813 sys_up_time: 100,
814 unix_secs: 1609459200,
815 unix_nsecs: 0,
816 flow_sequence: 1,
817 reserved: 0,
818 },
819 flowsets: vec![V7FlowSet {
820 src_addr: Ipv4Addr::new(192, 168, 1, 1),
821 dst_addr: Ipv4Addr::new(192, 168, 1, 2),
822 src_port: 1234,
823 dst_port: 80,
824 protocol_number: 6,
825 protocol_type: crate::protocol::ProtocolTypes::Tcp,
826 next_hop: Ipv4Addr::new(192, 168, 1, 254),
827 input: 0,
828 output: 0,
829 d_pkts: 10,
830 d_octets: 1000,
831 first: 100,
832 last: 200,
833 tcp_flags: 0,
834 tos: 0,
835 src_as: 0,
836 dst_as: 0,
837 src_mask: 0,
838 dst_mask: 0,
839 flags_fields_invalid: 0,
840 flags_fields_valid: 0,
841 router_src: Ipv4Addr::new(192, 168, 1, 254),
842 }],
843 };
844
845 let common: NetflowCommon = NetflowCommon::from(&v7);
846
847 assert_eq!(common.version, 7);
848 assert_eq!(common.timestamp, 100);
849 assert_eq!(common.flowsets.len(), 1);
850 let flowset = &common.flowsets[0];
851 assert_eq!(
852 flowset.src_addr.unwrap(),
853 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))
854 );
855 assert_eq!(
856 flowset.dst_addr.unwrap(),
857 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2))
858 );
859 assert_eq!(flowset.src_port.unwrap(), 1234);
860 assert_eq!(flowset.dst_port.unwrap(), 80);
861 assert_eq!(flowset.protocol_number.unwrap(), 6);
862 assert_eq!(
863 flowset.protocol_type.unwrap(),
864 crate::protocol::ProtocolTypes::Tcp
865 );
866 assert_eq!(flowset.first_seen.unwrap(), 100);
867 assert_eq!(flowset.last_seen.unwrap(), 200);
868 }
869
870 #[test]
871 fn it_converts_v9_to_common() {
872 let v9 = V9 {
874 header: V9Header {
875 version: 9,
876 count: 1,
877 sys_up_time: 100,
878 unix_secs: 1609459200,
879 sequence_number: 1,
880 source_id: 0,
881 },
882 flowsets: vec![V9FlowSet {
883 header: V9FlowSetHeader {
884 flowset_id: 0,
885 length: 0,
886 },
887 body: V9FlowSetBody::Data(V9Data {
888 padding: vec![],
889 fields: vec![Vec::from([
890 (
891 V9Field::Ipv4SrcAddr,
892 FieldValue::Ip4Addr(Ipv4Addr::new(192, 168, 1, 1)),
893 ),
894 (
895 V9Field::Ipv4DstAddr,
896 FieldValue::Ip4Addr(Ipv4Addr::new(192, 168, 1, 2)),
897 ),
898 (
899 V9Field::L4SrcPort,
900 FieldValue::DataNumber(DataNumber::U16(1234)),
901 ),
902 (
903 V9Field::L4DstPort,
904 FieldValue::DataNumber(DataNumber::U16(80)),
905 ),
906 (V9Field::Protocol, FieldValue::DataNumber(DataNumber::U8(6))),
907 (
908 V9Field::FirstSwitched,
909 FieldValue::DataNumber(DataNumber::U32(100)),
910 ),
911 (
912 V9Field::LastSwitched,
913 FieldValue::DataNumber(DataNumber::U32(200)),
914 ),
915 (
916 V9Field::InSrcMac,
917 FieldValue::MacAddr("00:00:00:00:00:01".to_string()),
918 ),
919 (
920 V9Field::InDstMac,
921 FieldValue::MacAddr("00:00:00:00:00:02".to_string()),
922 ),
923 ])],
924 }),
925 }],
926 };
927
928 let common: NetflowCommon = NetflowCommon::from(&v9);
929 assert_eq!(common.version, 9);
930 assert_eq!(common.timestamp, 100);
931 assert_eq!(common.flowsets.len(), 1);
932 let flowset = &common.flowsets[0];
933 assert_eq!(
934 flowset.src_addr.unwrap(),
935 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))
936 );
937 assert_eq!(
938 flowset.dst_addr.unwrap(),
939 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2))
940 );
941 assert_eq!(flowset.src_port.unwrap(), 1234);
942 assert_eq!(flowset.dst_port.unwrap(), 80);
943 assert_eq!(flowset.protocol_number.unwrap(), 6);
944 assert_eq!(
945 flowset.protocol_type.unwrap(),
946 crate::protocol::ProtocolTypes::Tcp
947 );
948 assert_eq!(flowset.first_seen.unwrap(), 100);
949 assert_eq!(flowset.last_seen.unwrap(), 200);
950 assert_eq!(flowset.src_mac.as_ref().unwrap(), "00:00:00:00:00:01");
951 assert_eq!(flowset.dst_mac.as_ref().unwrap(), "00:00:00:00:00:02");
952 }
953
954 #[test]
955 fn it_converts_ipfix_to_common() {
956 let ipfix = IPFix {
958 header: IPFixHeader {
959 version: 10,
960 length: 0,
961 export_time: 100,
962 sequence_number: 1,
963 observation_domain_id: 0,
964 },
965 flowsets: vec![IPFixFlowSet {
966 header: IPFixFlowSetHeader {
967 header_id: 0,
968 length: 0,
969 },
970 body: IPFixFlowSetBody::Data(IPFixData {
971 fields: vec![Vec::from([
972 (
973 IPFixField::IANA(IANAIPFixField::SourceIpv4address),
974 FieldValue::Ip4Addr(Ipv4Addr::new(192, 168, 1, 1)),
975 ),
976 (
977 IPFixField::IANA(IANAIPFixField::DestinationIpv4address),
978 FieldValue::Ip4Addr(Ipv4Addr::new(192, 168, 1, 2)),
979 ),
980 (
981 IPFixField::IANA(IANAIPFixField::SourceTransportPort),
982 FieldValue::DataNumber(DataNumber::U16(1234)),
983 ),
984 (
985 IPFixField::IANA(IANAIPFixField::DestinationTransportPort),
986 FieldValue::DataNumber(DataNumber::U16(80)),
987 ),
988 (
989 IPFixField::IANA(IANAIPFixField::ProtocolIdentifier),
990 FieldValue::DataNumber(DataNumber::U8(6)),
991 ),
992 (
993 IPFixField::IANA(IANAIPFixField::FlowStartSysUpTime),
994 FieldValue::DataNumber(DataNumber::U32(100)),
995 ),
996 (
997 IPFixField::IANA(IANAIPFixField::FlowEndSysUpTime),
998 FieldValue::DataNumber(DataNumber::U32(200)),
999 ),
1000 (
1001 IPFixField::IANA(IANAIPFixField::SourceMacaddress),
1002 FieldValue::MacAddr("00:00:00:00:00:01".to_string()),
1003 ),
1004 (
1005 IPFixField::IANA(IANAIPFixField::DestinationMacaddress),
1006 FieldValue::MacAddr("00:00:00:00:00:02".to_string()),
1007 ),
1008 ])],
1009 padding: vec![],
1010 }),
1011 }],
1012 };
1013
1014 let common: NetflowCommon = NetflowCommon::from(&ipfix);
1015 assert_eq!(common.version, 10);
1016 assert_eq!(common.timestamp, 100);
1017 assert_eq!(common.flowsets.len(), 1);
1018 let flowset = &common.flowsets[0];
1019 assert_eq!(
1020 flowset.src_addr.unwrap(),
1021 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))
1022 );
1023 assert_eq!(
1024 flowset.dst_addr.unwrap(),
1025 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2))
1026 );
1027 assert_eq!(flowset.src_port.unwrap(), 1234);
1028 assert_eq!(flowset.dst_port.unwrap(), 80);
1029 assert_eq!(flowset.protocol_number.unwrap(), 6);
1030 assert_eq!(
1031 flowset.protocol_type.unwrap(),
1032 crate::protocol::ProtocolTypes::Tcp
1033 );
1034 assert_eq!(flowset.first_seen.unwrap(), 100);
1035 assert_eq!(flowset.last_seen.unwrap(), 200);
1036 assert_eq!(flowset.src_mac.as_ref().unwrap(), "00:00:00:00:00:01");
1037 assert_eq!(flowset.dst_mac.as_ref().unwrap(), "00:00:00:00:00:02");
1038 }
1039
1040 #[test]
1041 fn it_converts_v9_to_common_with_custom_config() {
1042 use crate::netflow_common::V9FieldMappingConfig;
1043 use std::net::Ipv6Addr;
1044
1045 let v9 = V9 {
1047 header: V9Header {
1048 version: 9,
1049 count: 1,
1050 sys_up_time: 100,
1051 unix_secs: 1609459200,
1052 sequence_number: 1,
1053 source_id: 0,
1054 },
1055 flowsets: vec![V9FlowSet {
1056 header: V9FlowSetHeader {
1057 flowset_id: 0,
1058 length: 0,
1059 },
1060 body: V9FlowSetBody::Data(V9Data {
1061 padding: vec![],
1062 fields: vec![Vec::from([
1063 (
1064 V9Field::Ipv4SrcAddr,
1065 FieldValue::Ip4Addr(Ipv4Addr::new(192, 168, 1, 1)),
1066 ),
1067 (
1068 V9Field::Ipv6SrcAddr,
1069 FieldValue::Ip6Addr(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
1070 ),
1071 (
1072 V9Field::L4SrcPort,
1073 FieldValue::DataNumber(DataNumber::U16(1234)),
1074 ),
1075 ])],
1076 }),
1077 }],
1078 };
1079
1080 let default_config = V9FieldMappingConfig::default();
1082 let common = NetflowCommon::from_v9_with_config(&v9, &default_config);
1083 assert_eq!(
1084 common.flowsets[0].src_addr.unwrap(),
1085 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))
1086 );
1087
1088 let mut ipv6_config = V9FieldMappingConfig::default();
1090 ipv6_config.src_addr.primary = V9Field::Ipv6SrcAddr;
1091 ipv6_config.src_addr.fallback = Some(V9Field::Ipv4SrcAddr);
1092
1093 let common_ipv6 = NetflowCommon::from_v9_with_config(&v9, &ipv6_config);
1094 assert_eq!(
1095 common_ipv6.flowsets[0].src_addr.unwrap(),
1096 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1))
1097 );
1098 }
1099
1100 #[test]
1101 fn it_converts_ipfix_to_common_with_custom_config() {
1102 use crate::netflow_common::IPFixFieldMappingConfig;
1103 use std::net::Ipv6Addr;
1104
1105 let ipfix = IPFix {
1107 header: IPFixHeader {
1108 version: 10,
1109 length: 0,
1110 export_time: 100,
1111 sequence_number: 1,
1112 observation_domain_id: 0,
1113 },
1114 flowsets: vec![IPFixFlowSet {
1115 header: IPFixFlowSetHeader {
1116 header_id: 0,
1117 length: 0,
1118 },
1119 body: IPFixFlowSetBody::Data(IPFixData {
1120 fields: vec![Vec::from([
1121 (
1122 IPFixField::IANA(IANAIPFixField::SourceIpv4address),
1123 FieldValue::Ip4Addr(Ipv4Addr::new(192, 168, 1, 1)),
1124 ),
1125 (
1126 IPFixField::IANA(IANAIPFixField::SourceIpv6address),
1127 FieldValue::Ip6Addr(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
1128 ),
1129 (
1130 IPFixField::IANA(IANAIPFixField::SourceTransportPort),
1131 FieldValue::DataNumber(DataNumber::U16(1234)),
1132 ),
1133 ])],
1134 padding: vec![],
1135 }),
1136 }],
1137 };
1138
1139 let default_config = IPFixFieldMappingConfig::default();
1141 let common = NetflowCommon::from_ipfix_with_config(&ipfix, &default_config);
1142 assert_eq!(
1143 common.flowsets[0].src_addr.unwrap(),
1144 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))
1145 );
1146
1147 let mut ipv6_config = IPFixFieldMappingConfig::default();
1149 ipv6_config.src_addr.primary = IPFixField::IANA(IANAIPFixField::SourceIpv6address);
1150 ipv6_config.src_addr.fallback =
1151 Some(IPFixField::IANA(IANAIPFixField::SourceIpv4address));
1152
1153 let common_ipv6 = NetflowCommon::from_ipfix_with_config(&ipfix, &ipv6_config);
1154 assert_eq!(
1155 common_ipv6.flowsets[0].src_addr.unwrap(),
1156 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1))
1157 );
1158 }
1159
1160 #[test]
1161 fn it_uses_fallback_when_primary_not_found() {
1162 use crate::netflow_common::V9FieldMappingConfig;
1163
1164 let v9 = V9 {
1166 header: V9Header {
1167 version: 9,
1168 count: 1,
1169 sys_up_time: 100,
1170 unix_secs: 1609459200,
1171 sequence_number: 1,
1172 source_id: 0,
1173 },
1174 flowsets: vec![V9FlowSet {
1175 header: V9FlowSetHeader {
1176 flowset_id: 0,
1177 length: 0,
1178 },
1179 body: V9FlowSetBody::Data(V9Data {
1180 padding: vec![],
1181 fields: vec![Vec::from([(
1182 V9Field::Ipv4SrcAddr,
1183 FieldValue::Ip4Addr(Ipv4Addr::new(10, 0, 0, 1)),
1184 )])],
1185 }),
1186 }],
1187 };
1188
1189 let mut config = V9FieldMappingConfig::default();
1191 config.src_addr.primary = V9Field::Ipv6SrcAddr;
1192 config.src_addr.fallback = Some(V9Field::Ipv4SrcAddr);
1193
1194 let common = NetflowCommon::from_v9_with_config(&v9, &config);
1195 assert_eq!(
1197 common.flowsets[0].src_addr.unwrap(),
1198 IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))
1199 );
1200 }
1201}