netflow_parser/lib.rs
1#![doc = include_str!("../README.md")]
2
3#[cfg(feature = "netflow_common")]
4pub mod netflow_common;
5pub mod protocol;
6pub mod scoped_parser;
7pub mod static_versions;
8pub mod template_events;
9mod tests;
10pub mod variable_versions;
11
12#[cfg(feature = "netflow_common")]
13use crate::netflow_common::{NetflowCommon, NetflowCommonError, NetflowCommonFlowSet};
14
15use static_versions::{
16 v5::{V5, V5Parser},
17 v7::{V7, V7Parser},
18};
19use variable_versions::Config;
20use variable_versions::enterprise_registry::EnterpriseFieldDef;
21use variable_versions::ipfix::{IPFix, IPFixParser};
22use variable_versions::v9::{V9, V9Parser};
23
24use nom_derive::{Nom, Parse};
25use serde::Serialize;
26
27use std::collections::HashSet;
28
29// Re-export scoped parser types for convenience
30pub use scoped_parser::{
31 AutoScopedParser, IpfixSourceKey, RouterScopedParser, ScopingInfo, V9SourceKey,
32 extract_scoping_info,
33};
34
35// Re-export template event types for convenience
36pub use template_events::{TemplateEvent, TemplateHook, TemplateHooks, TemplateProtocol};
37
38// Re-export pending flows config for convenience
39pub use variable_versions::PendingFlowsConfig;
40
41/// Enum of supported Netflow Versions
42#[derive(Debug, Clone, Serialize)]
43pub enum NetflowPacket {
44 /// Version 5
45 V5(V5),
46 /// Version 7
47 V7(V7),
48 /// Version 9
49 V9(V9),
50 /// IPFix
51 IPFix(IPFix),
52}
53
54impl NetflowPacket {
55 pub fn is_v5(&self) -> bool {
56 matches!(self, Self::V5(_v))
57 }
58 pub fn is_v7(&self) -> bool {
59 matches!(self, Self::V7(_v))
60 }
61 pub fn is_v9(&self) -> bool {
62 matches!(self, Self::V9(_v))
63 }
64 pub fn is_ipfix(&self) -> bool {
65 matches!(self, Self::IPFix(_v))
66 }
67 #[cfg(feature = "netflow_common")]
68 pub fn as_netflow_common(&self) -> Result<NetflowCommon, NetflowCommonError> {
69 self.try_into()
70 }
71}
72
73/// Result of parsing NetFlow packets from a byte buffer.
74///
75/// This struct contains both successfully parsed packets and an optional error
76/// that stopped parsing. This ensures no data loss when parsing fails partway
77/// through a buffer.
78///
79/// # Examples
80///
81/// ```rust
82/// use netflow_parser::NetflowParser;
83///
84/// let mut parser = NetflowParser::default();
85/// let buffer = vec![/* netflow data */];
86///
87/// let result = parser.parse_bytes(&buffer);
88///
89/// // Process all successfully parsed packets
90/// for packet in result.packets {
91/// println!("Parsed packet");
92/// }
93///
94/// // Check if parsing stopped due to error
95/// if let Some(error) = result.error {
96/// eprintln!("Parsing stopped: {}", error);
97/// }
98/// ```
99#[derive(Debug, Clone, Serialize)]
100pub struct ParseResult {
101 /// Successfully parsed NetFlow packets.
102 /// This vec contains all packets that were successfully parsed before
103 /// any error occurred.
104 pub packets: Vec<NetflowPacket>,
105
106 /// Optional error that stopped parsing.
107 /// - `None` means all data was successfully parsed
108 /// - `Some(error)` means parsing stopped due to an error, but `packets`
109 /// contains all successfully parsed packets up to that point
110 pub error: Option<NetflowError>,
111}
112
113impl ParseResult {
114 /// Returns true if parsing completed without errors.
115 ///
116 /// # Examples
117 ///
118 /// ```rust
119 /// use netflow_parser::NetflowParser;
120 ///
121 /// let mut parser = NetflowParser::default();
122 /// let result = parser.parse_bytes(&[]);
123 /// assert!(result.is_ok());
124 /// ```
125 pub fn is_ok(&self) -> bool {
126 self.error.is_none()
127 }
128
129 /// Returns true if parsing stopped due to an error.
130 ///
131 /// Note: Even when this returns `true`, `packets` may contain
132 /// successfully parsed packets.
133 pub fn is_err(&self) -> bool {
134 self.error.is_some()
135 }
136}
137
138#[derive(Nom)]
139/// Generic Netflow Header for shared versions
140struct GenericNetflowHeader {
141 version: u16,
142}
143
144/// Main parser for Netflow packets supporting V5, V7, V9, and IPFIX.
145///
146/// Use [`NetflowParser::builder()`] for ergonomic configuration with the builder pattern,
147/// or [`NetflowParser::default()`] for quick setup with defaults.
148///
149/// # ⚠️ Multi-Source Deployments
150///
151/// **IMPORTANT**: If you're parsing NetFlow from multiple routers or sources,
152/// use [`AutoScopedParser`] instead of `NetflowParser`
153/// to prevent template cache collisions.
154///
155/// Template IDs are **NOT unique across sources**. Different routers can (and often do)
156/// use the same template ID with completely different schemas. When multiple sources
157/// share a single `NetflowParser`, their templates collide in the cache, causing:
158///
159/// - Template thrashing (constant eviction and re-learning)
160/// - Parsing failures (data parsed with wrong template)
161/// - Performance degradation (high cache miss rate)
162///
163/// ## Single Source (✅ Use NetflowParser)
164///
165/// ```rust
166/// use netflow_parser::NetflowParser;
167///
168/// let mut parser = NetflowParser::default();
169/// let data = [0u8; 72]; // Example NetFlow data
170/// // Single router/source - no collisions possible
171/// let packets = parser.parse_bytes(&data);
172/// ```
173///
174/// ## Multiple Sources (✅ Use AutoScopedParser)
175///
176/// ```rust
177/// use netflow_parser::AutoScopedParser;
178/// use std::net::SocketAddr;
179///
180/// let mut parser = AutoScopedParser::new();
181/// let source: SocketAddr = "192.168.1.1:2055".parse().unwrap();
182/// let data = [0u8; 72]; // Example NetFlow data
183/// // Each source gets isolated template cache (RFC-compliant)
184/// let packets = parser.parse_from_source(source, &data);
185/// ```
186///
187/// # Examples
188///
189/// ```rust
190/// use netflow_parser::NetflowParser;
191/// use netflow_parser::variable_versions::ttl::TtlConfig;
192/// use std::time::Duration;
193///
194/// // Using builder pattern (recommended)
195/// let parser = NetflowParser::builder()
196/// .with_cache_size(2000)
197/// .with_ttl(TtlConfig::new(Duration::from_secs(7200)))
198/// .build()
199/// .expect("Failed to build parser");
200///
201/// // Using default
202/// let parser = NetflowParser::default();
203/// ```
204#[derive(Debug)]
205pub struct NetflowParser {
206 pub v9_parser: V9Parser,
207 pub ipfix_parser: IPFixParser,
208 pub allowed_versions: HashSet<u16>,
209 /// Maximum number of bytes to include in error samples to prevent memory exhaustion.
210 /// Defaults to 256 bytes.
211 pub max_error_sample_size: usize,
212 /// Template event hooks for monitoring template lifecycle events.
213 template_hooks: TemplateHooks,
214}
215
216/// Statistics about template cache utilization.
217#[derive(Debug, Clone)]
218pub struct CacheStats {
219 /// Current number of cached templates
220 pub current_size: usize,
221 /// Maximum cache size before LRU eviction
222 pub max_size: usize,
223 /// TTL configuration (if enabled)
224 pub ttl_config: Option<variable_versions::ttl::TtlConfig>,
225 /// Performance metrics snapshot
226 pub metrics: variable_versions::metrics::CacheMetricsSnapshot,
227 /// Number of flows currently cached as pending (awaiting template)
228 pub pending_flow_count: usize,
229}
230
231/// Combined cache statistics for both V9 and IPFIX template caches.
232///
233/// This struct provides named fields instead of positional tuples,
234/// making it clear which stats belong to V9 vs IPFIX.
235#[derive(Debug, Clone)]
236pub struct ParserCacheStats {
237 /// V9 template cache statistics
238 pub v9: CacheStats,
239 /// IPFIX template cache statistics
240 pub ipfix: CacheStats,
241}
242
243/// Builder for configuring and constructing a [`NetflowParser`].
244///
245/// # Examples
246///
247/// ```rust
248/// use netflow_parser::NetflowParser;
249/// use netflow_parser::variable_versions::ttl::TtlConfig;
250/// use std::collections::HashSet;
251/// use std::time::Duration;
252///
253/// let parser = NetflowParser::builder()
254/// .with_cache_size(2000)
255/// .with_ttl(TtlConfig::new(Duration::from_secs(7200)))
256/// .with_allowed_versions([5, 9, 10].into())
257/// .with_max_error_sample_size(512)
258/// .build()
259/// .expect("Failed to build parser");
260/// ```
261#[derive(Clone)]
262pub struct NetflowParserBuilder {
263 v9_config: Config,
264 ipfix_config: Config,
265 allowed_versions: HashSet<u16>,
266 max_error_sample_size: usize,
267 template_hooks: TemplateHooks,
268}
269
270// Custom Debug implementation to avoid printing closures
271impl std::fmt::Debug for NetflowParserBuilder {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 f.debug_struct("NetflowParserBuilder")
274 .field("v9_config", &self.v9_config)
275 .field("ipfix_config", &self.ipfix_config)
276 .field("allowed_versions", &self.allowed_versions)
277 .field("max_error_sample_size", &self.max_error_sample_size)
278 .field(
279 "template_hooks",
280 &format!("{} hooks", self.template_hooks.len()),
281 )
282 .finish()
283 }
284}
285
286impl Default for NetflowParserBuilder {
287 fn default() -> Self {
288 Self {
289 v9_config: Config::new(1000, None),
290 ipfix_config: Config::new(1000, None),
291 allowed_versions: [5, 7, 9, 10].iter().cloned().collect(),
292 max_error_sample_size: 256,
293 template_hooks: TemplateHooks::new(),
294 }
295 }
296}
297
298impl NetflowParserBuilder {
299 /// Sets the template cache size for both V9 and IPFIX parsers.
300 ///
301 /// # Arguments
302 ///
303 /// * `size` - Maximum number of templates to cache (must be > 0)
304 ///
305 /// # Examples
306 ///
307 /// ```rust
308 /// use netflow_parser::NetflowParser;
309 ///
310 /// let parser = NetflowParser::builder()
311 /// .with_cache_size(2000)
312 /// .build()
313 /// .expect("Failed to build parser");
314 /// ```
315 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
316 pub fn with_cache_size(mut self, size: usize) -> Self {
317 self.v9_config.max_template_cache_size = size;
318 self.ipfix_config.max_template_cache_size = size;
319 self
320 }
321
322 /// Sets the V9 parser template cache size independently.
323 ///
324 /// # Arguments
325 ///
326 /// * `size` - Maximum number of templates to cache (must be > 0)
327 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
328 pub fn with_v9_cache_size(mut self, size: usize) -> Self {
329 self.v9_config.max_template_cache_size = size;
330 self
331 }
332
333 /// Sets the IPFIX parser template cache size independently.
334 ///
335 /// * `size` - Maximum number of templates to cache (must be > 0)
336 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
337 pub fn with_ipfix_cache_size(mut self, size: usize) -> Self {
338 self.ipfix_config.max_template_cache_size = size;
339 self
340 }
341
342 /// Sets the maximum field count for both V9 and IPFIX parsers.
343 ///
344 /// This limits the number of fields allowed in a single template to prevent DoS attacks.
345 ///
346 /// # Arguments
347 ///
348 /// * `count` - Maximum number of fields per template (default: 10,000)
349 ///
350 /// # Examples
351 ///
352 /// ```rust
353 /// use netflow_parser::NetflowParser;
354 ///
355 /// let parser = NetflowParser::builder()
356 /// .with_max_field_count(5000) // More restrictive limit
357 /// .build()
358 /// .expect("Failed to build parser");
359 /// ```
360 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
361 pub fn with_max_field_count(mut self, count: usize) -> Self {
362 self.v9_config.max_field_count = count;
363 self.ipfix_config.max_field_count = count;
364 self
365 }
366
367 /// Sets the V9 parser maximum field count independently.
368 ///
369 /// # Arguments
370 ///
371 /// * `count` - Maximum number of fields per template
372 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
373 pub fn with_v9_max_field_count(mut self, count: usize) -> Self {
374 self.v9_config.max_field_count = count;
375 self
376 }
377
378 /// Sets the IPFIX parser maximum field count independently.
379 ///
380 /// # Arguments
381 ///
382 /// * `count` - Maximum number of fields per template
383 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
384 pub fn with_ipfix_max_field_count(mut self, count: usize) -> Self {
385 self.ipfix_config.max_field_count = count;
386 self
387 }
388
389 /// Sets the TTL configuration for both V9 and IPFIX parsers.
390 ///
391 /// # Arguments
392 ///
393 /// * `ttl` - TTL configuration (time-based)
394 ///
395 /// # Examples
396 ///
397 /// ```rust
398 /// use netflow_parser::NetflowParser;
399 /// use netflow_parser::variable_versions::ttl::TtlConfig;
400 /// use std::time::Duration;
401 ///
402 /// let parser = NetflowParser::builder()
403 /// .with_ttl(TtlConfig::new(Duration::from_secs(7200)))
404 /// .build()
405 /// .expect("Failed to build parser");
406 /// ```
407 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
408 pub fn with_ttl(mut self, ttl: variable_versions::ttl::TtlConfig) -> Self {
409 self.v9_config.ttl_config = Some(ttl.clone());
410 self.ipfix_config.ttl_config = Some(ttl);
411 self
412 }
413
414 /// Sets the TTL configuration for V9 parser independently.
415 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
416 pub fn with_v9_ttl(mut self, ttl: variable_versions::ttl::TtlConfig) -> Self {
417 self.v9_config.ttl_config = Some(ttl);
418 self
419 }
420
421 /// Sets the TTL configuration for IPFIX parser independently.
422 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
423 pub fn with_ipfix_ttl(mut self, ttl: variable_versions::ttl::TtlConfig) -> Self {
424 self.ipfix_config.ttl_config = Some(ttl);
425 self
426 }
427
428 /// Sets which Netflow versions are allowed to be parsed.
429 ///
430 /// # Arguments
431 ///
432 /// * `versions` - Set of allowed version numbers (5, 7, 9, 10)
433 ///
434 /// # Examples
435 ///
436 /// ```rust
437 /// use netflow_parser::NetflowParser;
438 ///
439 /// // Only parse V9 and IPFIX
440 /// let parser = NetflowParser::builder()
441 /// .with_allowed_versions([9, 10].into())
442 /// .build()
443 /// .expect("Failed to build parser");
444 /// ```
445 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
446 pub fn with_allowed_versions(mut self, versions: HashSet<u16>) -> Self {
447 self.allowed_versions = versions;
448 self
449 }
450
451 /// Sets the maximum error sample size for error reporting.
452 ///
453 /// # Arguments
454 ///
455 /// * `size` - Maximum bytes to include in error messages
456 ///
457 /// # Examples
458 ///
459 /// ```rust
460 /// use netflow_parser::NetflowParser;
461 ///
462 /// let parser = NetflowParser::builder()
463 /// .with_max_error_sample_size(512)
464 /// .build()
465 /// .expect("Failed to build parser");
466 /// ```
467 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
468 pub fn with_max_error_sample_size(mut self, size: usize) -> Self {
469 self.max_error_sample_size = size;
470 self.v9_config.max_error_sample_size = size;
471 self.ipfix_config.max_error_sample_size = size;
472 self
473 }
474
475 /// Registers a custom enterprise field definition for both V9 and IPFIX parsers.
476 ///
477 /// This allows library users to define their own enterprise-specific fields without
478 /// modifying the library source code. Registered fields will be parsed according to
479 /// their specified data type instead of falling back to raw bytes.
480 ///
481 /// # Arguments
482 ///
483 /// * `def` - Enterprise field definition containing enterprise number, field number, name, and data type
484 ///
485 /// # Examples
486 ///
487 /// ```rust
488 /// use netflow_parser::NetflowParser;
489 /// use netflow_parser::variable_versions::enterprise_registry::EnterpriseFieldDef;
490 /// use netflow_parser::variable_versions::data_number::FieldDataType;
491 ///
492 /// let parser = NetflowParser::builder()
493 /// .register_enterprise_field(EnterpriseFieldDef::new(
494 /// 12345, // Enterprise number
495 /// 1, // Field number
496 /// "customMetric",
497 /// FieldDataType::UnsignedDataNumber,
498 /// ))
499 /// .register_enterprise_field(EnterpriseFieldDef::new(
500 /// 12345,
501 /// 2,
502 /// "customApplicationName",
503 /// FieldDataType::String,
504 /// ))
505 /// .build()
506 /// .expect("Failed to build parser");
507 /// ```
508 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
509 pub fn register_enterprise_field(mut self, def: EnterpriseFieldDef) -> Self {
510 self.v9_config.enterprise_registry.register(def.clone());
511 self.ipfix_config.enterprise_registry.register(def);
512 self
513 }
514
515 /// Registers multiple custom enterprise field definitions at once.
516 ///
517 /// # Arguments
518 ///
519 /// * `defs` - Iterator of enterprise field definitions
520 ///
521 /// # Examples
522 ///
523 /// ```rust
524 /// use netflow_parser::NetflowParser;
525 /// use netflow_parser::variable_versions::enterprise_registry::EnterpriseFieldDef;
526 /// use netflow_parser::variable_versions::data_number::FieldDataType;
527 ///
528 /// let fields = vec![
529 /// EnterpriseFieldDef::new(12345, 1, "field1", FieldDataType::UnsignedDataNumber),
530 /// EnterpriseFieldDef::new(12345, 2, "field2", FieldDataType::String),
531 /// EnterpriseFieldDef::new(12345, 3, "field3", FieldDataType::Ip4Addr),
532 /// ];
533 ///
534 /// let parser = NetflowParser::builder()
535 /// .register_enterprise_fields(fields)
536 /// .build()
537 /// .expect("Failed to build parser");
538 /// ```
539 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
540 pub fn register_enterprise_fields(
541 mut self,
542 defs: impl IntoIterator<Item = EnterpriseFieldDef>,
543 ) -> Self {
544 // Collect once to avoid unnecessary cloning
545 let defs: Vec<_> = defs.into_iter().collect();
546
547 // Register clones in V9 registry
548 for def in &defs {
549 self.v9_config.enterprise_registry.register(def.clone());
550 }
551
552 // Move originals into IPFIX registry (no clone needed)
553 for def in defs {
554 self.ipfix_config.enterprise_registry.register(def);
555 }
556
557 self
558 }
559
560 /// Enables pending flow caching for both V9 and IPFIX parsers.
561 ///
562 /// When enabled, flows that arrive before their template are cached.
563 /// When the template later arrives, cached flows are automatically
564 /// re-parsed and included in the output.
565 ///
566 /// # Arguments
567 ///
568 /// * `config` - Pending flows configuration (cache size and optional TTL)
569 ///
570 /// # Examples
571 ///
572 /// ```rust
573 /// use netflow_parser::{NetflowParser, PendingFlowsConfig};
574 ///
575 /// let parser = NetflowParser::builder()
576 /// .with_pending_flows(PendingFlowsConfig::default())
577 /// .build()
578 /// .expect("Failed to build parser");
579 /// ```
580 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
581 pub fn with_pending_flows(mut self, config: PendingFlowsConfig) -> Self {
582 self.v9_config.pending_flows_config = Some(config.clone());
583 self.ipfix_config.pending_flows_config = Some(config);
584 self
585 }
586
587 /// Enables pending flow caching for the V9 parser only.
588 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
589 pub fn with_v9_pending_flows(mut self, config: PendingFlowsConfig) -> Self {
590 self.v9_config.pending_flows_config = Some(config);
591 self
592 }
593
594 /// Enables pending flow caching for the IPFIX parser only.
595 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
596 pub fn with_ipfix_pending_flows(mut self, config: PendingFlowsConfig) -> Self {
597 self.ipfix_config.pending_flows_config = Some(config);
598 self
599 }
600
601 /// Hint for single-source deployments (documentation only).
602 ///
603 /// This method exists for clarity and returns `self` unchanged.
604 /// Use when parsing from a single router or source.
605 ///
606 /// # Examples
607 ///
608 /// ```rust
609 /// use netflow_parser::NetflowParser;
610 ///
611 /// let parser = NetflowParser::builder()
612 /// .single_source() // Documents intent
613 /// .with_cache_size(1000)
614 /// .build()
615 /// .expect("Failed to build parser");
616 /// ```
617 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
618 pub fn single_source(self) -> Self {
619 self
620 }
621
622 /// Creates an AutoScopedParser for multi-source deployments.
623 ///
624 /// **Recommended** for parsing NetFlow from multiple routers. Each source
625 /// gets an isolated template cache, preventing template ID collisions.
626 ///
627 /// # Examples
628 ///
629 /// ```rust
630 /// use netflow_parser::NetflowParser;
631 /// use std::net::SocketAddr;
632 ///
633 /// // Multi-source deployment
634 /// let mut parser = NetflowParser::builder()
635 /// .with_cache_size(2000)
636 /// .multi_source();
637 ///
638 /// let source: SocketAddr = "192.168.1.1:2055".parse().unwrap();
639 /// let data = [0u8; 72]; // Example data
640 /// let packets = parser.parse_from_source(source, &data);
641 /// ```
642 ///
643 /// Equivalent to:
644 /// ```rust
645 /// use netflow_parser::{NetflowParser, AutoScopedParser};
646 ///
647 /// let builder = NetflowParser::builder().with_cache_size(2000);
648 /// let _parser = AutoScopedParser::with_builder(builder);
649 /// ```
650 pub fn multi_source(self) -> AutoScopedParser {
651 AutoScopedParser::with_builder(self)
652 }
653
654 /// Builds the configured [`NetflowParser`].
655 ///
656 /// # Errors
657 ///
658 /// Returns an error if:
659 /// - Template cache size is 0
660 /// - Parser initialization fails
661 ///
662 /// # Examples
663 ///
664 /// ```rust
665 /// use netflow_parser::NetflowParser;
666 ///
667 /// let parser = NetflowParser::builder()
668 /// .with_cache_size(2000)
669 /// .build()
670 /// .expect("Failed to build parser");
671 /// ```
672 /// Registers a callback for template lifecycle events.
673 ///
674 /// This allows you to monitor template operations in real-time, including:
675 /// - Template learning (new templates added to cache)
676 /// - Template collisions (template ID reused)
677 /// - Template evictions (LRU policy removed template)
678 /// - Template expirations (TTL-based removal)
679 /// - Missing templates (data packet for unknown template)
680 ///
681 /// # Arguments
682 ///
683 /// * `hook` - A closure that will be called for each template event
684 ///
685 /// # Examples
686 ///
687 /// ```rust
688 /// use netflow_parser::{NetflowParser, TemplateEvent, TemplateProtocol};
689 ///
690 /// let parser = NetflowParser::builder()
691 /// .on_template_event(|event| {
692 /// match event {
693 /// TemplateEvent::Learned { template_id, protocol } => {
694 /// println!("Learned template {}", template_id);
695 /// }
696 /// TemplateEvent::Collision { template_id, protocol } => {
697 /// eprintln!("⚠️ Template collision: {}", template_id);
698 /// }
699 /// _ => {}
700 /// }
701 /// })
702 /// .build()
703 /// .unwrap();
704 /// ```
705 #[must_use = "builder methods consume self and return a new builder; the return value must be used"]
706 pub fn on_template_event<F>(mut self, hook: F) -> Self
707 where
708 F: Fn(&TemplateEvent) + Send + Sync + 'static,
709 {
710 self.template_hooks.register(hook);
711 self
712 }
713
714 /// Builds the `NetflowParser` with the configured settings.
715 ///
716 /// Returns an error if the parser configuration is invalid.
717 pub fn build(self) -> Result<NetflowParser, String> {
718 let v9_parser =
719 V9Parser::try_new(self.v9_config).map_err(|e| format!("V9 parser error: {}", e))?;
720 let ipfix_parser = IPFixParser::try_new(self.ipfix_config)
721 .map_err(|e| format!("IPFIX parser error: {}", e))?;
722
723 Ok(NetflowParser {
724 v9_parser,
725 ipfix_parser,
726 allowed_versions: self.allowed_versions,
727 max_error_sample_size: self.max_error_sample_size,
728 template_hooks: self.template_hooks,
729 })
730 }
731}
732
733#[derive(Debug, Clone)]
734pub enum ParsedNetflow<'a> {
735 Success {
736 packet: NetflowPacket,
737 remaining: &'a [u8],
738 },
739 Error {
740 error: NetflowError,
741 },
742 UnallowedVersion,
743}
744
745/// Comprehensive error type for NetFlow parsing operations.
746///
747/// Provides rich context about parsing failures including offset, error kind,
748/// and relevant data for debugging.
749#[derive(Debug, Clone, Serialize)]
750pub enum NetflowError {
751 /// Incomplete data - more bytes needed to parse a complete packet.
752 ///
753 /// Contains the number of bytes available and a description of what was expected.
754 Incomplete {
755 /// Number of bytes that were available
756 available: usize,
757 /// Description of what was being parsed
758 context: String,
759 },
760
761 /// Unknown or unsupported NetFlow version encountered.
762 ///
763 /// The version number found in the packet header doesn't match any known
764 /// NetFlow version (V5, V7, V9, IPFIX).
765 UnsupportedVersion {
766 /// The version number found in the packet
767 version: u16,
768 /// Offset in bytes where the version was found
769 offset: usize,
770 /// Sample of the packet data for debugging
771 sample: Vec<u8>,
772 },
773
774 /// Version is valid but filtered out by `allowed_versions` configuration.
775 ///
776 /// The parser was configured to only accept certain versions and this
777 /// version was explicitly excluded.
778 FilteredVersion {
779 /// The version number that was filtered
780 version: u16,
781 },
782
783 /// Template definition is required but not found in cache.
784 ///
785 /// For V9 and IPFIX, data packets reference template IDs that must be
786 /// learned from template packets. This error occurs when data arrives
787 /// before (or without) its corresponding template.
788 MissingTemplate {
789 /// The template ID that was not found
790 template_id: u16,
791 /// The protocol (V9 or IPFIX)
792 protocol: TemplateProtocol,
793 /// List of currently cached template IDs for this protocol
794 available_templates: Vec<u16>,
795 /// Raw packet data that couldn't be parsed
796 raw_data: Vec<u8>,
797 },
798
799 /// Parsing error with detailed context.
800 ///
801 /// Generic parsing failure with information about what failed and where.
802 ParseError {
803 /// Offset in bytes where the error occurred
804 offset: usize,
805 /// Description of what was being parsed
806 context: String,
807 /// The specific error kind
808 kind: String,
809 /// Sample of remaining data for debugging
810 remaining: Vec<u8>,
811 },
812
813 /// Partial parse - some data was parsed but errors occurred.
814 ///
815 /// Used when processing continues despite errors (e.g., some flowsets
816 /// parsed successfully but others failed).
817 Partial {
818 /// Description of the partial parse result
819 message: String,
820 },
821}
822
823impl std::fmt::Display for NetflowError {
824 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
825 match self {
826 NetflowError::Incomplete { available, context } => {
827 write!(
828 f,
829 "Incomplete data: {} (only {} bytes available)",
830 context, available
831 )
832 }
833 NetflowError::UnsupportedVersion {
834 version, offset, ..
835 } => {
836 write!(
837 f,
838 "Unsupported NetFlow version {} at offset {}",
839 version, offset
840 )
841 }
842 NetflowError::FilteredVersion { version } => {
843 write!(
844 f,
845 "NetFlow version {} filtered out by allowed_versions configuration",
846 version
847 )
848 }
849 NetflowError::MissingTemplate {
850 template_id,
851 protocol,
852 available_templates,
853 ..
854 } => {
855 write!(
856 f,
857 "Missing template {} for {:?} (available: {:?})",
858 template_id, protocol, available_templates
859 )
860 }
861 NetflowError::ParseError {
862 offset,
863 context,
864 kind,
865 ..
866 } => {
867 write!(
868 f,
869 "Parse error at offset {}: {} ({})",
870 offset, context, kind
871 )
872 }
873 NetflowError::Partial { message } => {
874 write!(f, "Partial parse error: {}", message)
875 }
876 }
877 }
878}
879
880impl std::error::Error for NetflowError {}
881
882// Legacy type alias for backwards compatibility during migration
883#[deprecated(since = "0.8.0", note = "Use NetflowError instead")]
884pub type NetflowPacketError = NetflowError;
885
886#[deprecated(since = "0.8.0", note = "Use NetflowError instead")]
887pub type NetflowParseError = NetflowError;
888
889#[derive(Debug, Clone, Serialize)]
890pub struct PartialParse {
891 pub version: u16,
892 pub remaining: Vec<u8>,
893 pub error: String,
894}
895
896/// Iterator that yields NetflowPacket items from a byte buffer without allocating a Vec.
897/// Maintains parser state for template caching (V9/IPFIX).
898pub struct NetflowPacketIterator<'a> {
899 parser: &'a mut NetflowParser,
900 remaining: &'a [u8],
901 errored: bool,
902}
903
904impl<'a> NetflowPacketIterator<'a> {
905 /// Returns the unconsumed bytes remaining in the buffer.
906 ///
907 /// This is useful for:
908 /// - Debugging: See how much data was consumed
909 /// - Mixed protocols: Process non-netflow data after netflow packets
910 /// - Resumption: Know where parsing stopped
911 ///
912 /// # Examples
913 ///
914 /// ```rust
915 /// use netflow_parser::NetflowParser;
916 ///
917 /// let v5_packet = [0, 5, 0, 1, 3, 0, 4, 0, 5, 0, 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, 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, 2, 3, 4, 5, 6, 7,];
918 /// let mut parser = NetflowParser::default();
919 /// let mut iter = parser.iter_packets(&v5_packet);
920 ///
921 /// while let Some(_packet) = iter.next() {
922 /// // Process packet
923 /// }
924 ///
925 /// // Check how many bytes remain unconsumed
926 /// assert_eq!(iter.remaining().len(), 0);
927 /// ```
928 pub fn remaining(&self) -> &'a [u8] {
929 self.remaining
930 }
931
932 /// Returns true if all bytes have been consumed or an error occurred.
933 ///
934 /// This is useful for validation and ensuring complete buffer processing.
935 ///
936 /// # Examples
937 ///
938 /// ```rust
939 /// use netflow_parser::NetflowParser;
940 ///
941 /// let v5_packet = [0, 5, 0, 1, 3, 0, 4, 0, 5, 0, 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, 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, 2, 3, 4, 5, 6, 7,];
942 /// let mut parser = NetflowParser::default();
943 /// let mut iter = parser.iter_packets(&v5_packet);
944 ///
945 /// // Consume all packets
946 /// for _packet in &mut iter {
947 /// // Process packet
948 /// }
949 ///
950 /// assert!(iter.is_complete());
951 /// ```
952 pub fn is_complete(&self) -> bool {
953 self.remaining.is_empty() || self.errored
954 }
955}
956
957impl<'a> Iterator for NetflowPacketIterator<'a> {
958 type Item = Result<NetflowPacket, NetflowError>;
959
960 fn next(&mut self) -> Option<Self::Item> {
961 // Stop if we've errored or no bytes remain
962 if self.errored || self.remaining.is_empty() {
963 return None;
964 }
965
966 match self.parser.parse_packet_by_version(self.remaining) {
967 ParsedNetflow::Success {
968 packet,
969 remaining: new_remaining,
970 } => {
971 self.remaining = new_remaining;
972 Some(Ok(packet))
973 }
974 ParsedNetflow::UnallowedVersion => {
975 self.errored = true;
976 None
977 }
978 ParsedNetflow::Error { error } => {
979 self.errored = true;
980 Some(Err(error))
981 }
982 }
983 }
984}
985
986impl Default for NetflowParser {
987 fn default() -> Self {
988 Self {
989 v9_parser: V9Parser::default(),
990 ipfix_parser: IPFixParser::default(),
991 allowed_versions: [5, 7, 9, 10].iter().cloned().collect(),
992 max_error_sample_size: 256,
993 template_hooks: TemplateHooks::new(),
994 }
995 }
996}
997
998impl NetflowParser {
999 /// Creates a new builder for configuring a [`NetflowParser`].
1000 ///
1001 /// # Examples
1002 ///
1003 /// ```rust
1004 /// use netflow_parser::NetflowParser;
1005 /// use netflow_parser::variable_versions::ttl::TtlConfig;
1006 /// use std::time::Duration;
1007 ///
1008 /// let parser = NetflowParser::builder()
1009 /// .with_cache_size(2000)
1010 /// .with_ttl(TtlConfig::new(Duration::from_secs(7200)))
1011 /// .build()
1012 /// .expect("Failed to build parser");
1013 /// ```
1014 pub fn builder() -> NetflowParserBuilder {
1015 NetflowParserBuilder::default()
1016 }
1017
1018 /// Gets statistics about the V9 template cache.
1019 ///
1020 /// # Examples
1021 ///
1022 /// ```rust
1023 /// use netflow_parser::NetflowParser;
1024 ///
1025 /// let parser = NetflowParser::default();
1026 /// let stats = parser.v9_cache_stats();
1027 /// println!("V9 cache: {}/{} templates", stats.current_size, stats.max_size);
1028 /// ```
1029 pub fn v9_cache_stats(&self) -> CacheStats {
1030 CacheStats {
1031 current_size: self.v9_parser.templates.len()
1032 + self.v9_parser.options_templates.len(),
1033 max_size: self.v9_parser.max_template_cache_size,
1034 ttl_config: self.v9_parser.ttl_config.clone(),
1035 metrics: self.v9_parser.metrics.snapshot(),
1036 pending_flow_count: self.v9_parser.pending_flow_count(),
1037 }
1038 }
1039
1040 /// Gets statistics about the IPFIX template cache.
1041 ///
1042 /// # Examples
1043 ///
1044 /// ```rust
1045 /// use netflow_parser::NetflowParser;
1046 ///
1047 /// let parser = NetflowParser::default();
1048 /// let stats = parser.ipfix_cache_stats();
1049 /// println!("IPFIX cache: {}/{} templates", stats.current_size, stats.max_size);
1050 /// ```
1051 pub fn ipfix_cache_stats(&self) -> CacheStats {
1052 CacheStats {
1053 current_size: self.ipfix_parser.templates.len()
1054 + self.ipfix_parser.v9_templates.len()
1055 + self.ipfix_parser.ipfix_options_templates.len()
1056 + self.ipfix_parser.v9_options_templates.len(),
1057 max_size: self.ipfix_parser.max_template_cache_size,
1058 ttl_config: self.ipfix_parser.ttl_config.clone(),
1059 metrics: self.ipfix_parser.metrics.snapshot(),
1060 pending_flow_count: self.ipfix_parser.pending_flow_count(),
1061 }
1062 }
1063
1064 /// Lists all cached V9 template IDs.
1065 ///
1066 /// Note: This returns template IDs from both regular and options templates.
1067 ///
1068 /// # Examples
1069 ///
1070 /// ```rust
1071 /// use netflow_parser::NetflowParser;
1072 ///
1073 /// let parser = NetflowParser::default();
1074 /// let template_ids = parser.v9_template_ids();
1075 /// println!("Cached V9 templates: {:?}", template_ids);
1076 /// ```
1077 pub fn v9_template_ids(&self) -> Vec<u16> {
1078 let mut ids: Vec<u16> = self.v9_parser.templates.iter().map(|(id, _)| *id).collect();
1079 ids.extend(self.v9_parser.options_templates.iter().map(|(id, _)| *id));
1080 ids.sort_unstable();
1081 ids
1082 }
1083
1084 /// Lists all cached IPFIX template IDs.
1085 ///
1086 /// Note: This returns template IDs from both IPFIX and V9-format templates (IPFIX can contain both).
1087 ///
1088 /// # Examples
1089 ///
1090 /// ```rust
1091 /// use netflow_parser::NetflowParser;
1092 ///
1093 /// let parser = NetflowParser::default();
1094 /// let template_ids = parser.ipfix_template_ids();
1095 /// println!("Cached IPFIX templates: {:?}", template_ids);
1096 /// ```
1097 pub fn ipfix_template_ids(&self) -> Vec<u16> {
1098 let mut ids: Vec<u16> = self
1099 .ipfix_parser
1100 .templates
1101 .iter()
1102 .map(|(id, _)| *id)
1103 .collect();
1104 ids.extend(self.ipfix_parser.v9_templates.iter().map(|(id, _)| *id));
1105 ids.extend(
1106 self.ipfix_parser
1107 .ipfix_options_templates
1108 .iter()
1109 .map(|(id, _)| *id),
1110 );
1111 ids.extend(
1112 self.ipfix_parser
1113 .v9_options_templates
1114 .iter()
1115 .map(|(id, _)| *id),
1116 );
1117 ids.sort_unstable();
1118 ids
1119 }
1120
1121 /// Checks if a V9 template with the given ID is cached.
1122 ///
1123 /// Note: This uses `peek()` which does not affect LRU ordering.
1124 ///
1125 /// # Arguments
1126 ///
1127 /// * `template_id` - The template ID to check
1128 ///
1129 /// # Examples
1130 ///
1131 /// ```rust
1132 /// use netflow_parser::NetflowParser;
1133 ///
1134 /// let parser = NetflowParser::default();
1135 /// if parser.has_v9_template(256) {
1136 /// println!("Template 256 is cached");
1137 /// }
1138 /// ```
1139 pub fn has_v9_template(&self, template_id: u16) -> bool {
1140 self.v9_parser.templates.peek(&template_id).is_some()
1141 || self
1142 .v9_parser
1143 .options_templates
1144 .peek(&template_id)
1145 .is_some()
1146 }
1147
1148 /// Checks if an IPFIX template with the given ID is cached.
1149 ///
1150 /// Note: This uses `peek()` which does not affect LRU ordering.
1151 ///
1152 /// # Arguments
1153 ///
1154 /// * `template_id` - The template ID to check
1155 ///
1156 /// # Examples
1157 ///
1158 /// ```rust
1159 /// use netflow_parser::NetflowParser;
1160 ///
1161 /// let parser = NetflowParser::default();
1162 /// if parser.has_ipfix_template(256) {
1163 /// println!("Template 256 is cached");
1164 /// }
1165 /// ```
1166 pub fn has_ipfix_template(&self, template_id: u16) -> bool {
1167 self.ipfix_parser.templates.peek(&template_id).is_some()
1168 || self.ipfix_parser.v9_templates.peek(&template_id).is_some()
1169 || self
1170 .ipfix_parser
1171 .ipfix_options_templates
1172 .peek(&template_id)
1173 .is_some()
1174 || self
1175 .ipfix_parser
1176 .v9_options_templates
1177 .peek(&template_id)
1178 .is_some()
1179 }
1180
1181 /// Clears all cached V9 templates.
1182 ///
1183 /// This is useful for testing or when you need to force template re-learning.
1184 ///
1185 /// # Examples
1186 ///
1187 /// ```rust
1188 /// use netflow_parser::NetflowParser;
1189 ///
1190 /// let mut parser = NetflowParser::default();
1191 /// parser.clear_v9_templates();
1192 /// ```
1193 pub fn clear_v9_templates(&mut self) {
1194 self.v9_parser.templates.clear();
1195 self.v9_parser.options_templates.clear();
1196 }
1197
1198 /// Clears all cached IPFIX templates.
1199 ///
1200 /// This is useful for testing or when you need to force template re-learning.
1201 ///
1202 /// # Examples
1203 ///
1204 /// ```rust
1205 /// use netflow_parser::NetflowParser;
1206 ///
1207 /// let mut parser = NetflowParser::default();
1208 /// parser.clear_ipfix_templates();
1209 /// ```
1210 pub fn clear_ipfix_templates(&mut self) {
1211 self.ipfix_parser.templates.clear();
1212 self.ipfix_parser.v9_templates.clear();
1213 self.ipfix_parser.ipfix_options_templates.clear();
1214 self.ipfix_parser.v9_options_templates.clear();
1215 }
1216
1217 /// Clears all pending V9 flows.
1218 pub fn clear_v9_pending_flows(&mut self) {
1219 self.v9_parser.clear_pending_flows();
1220 }
1221
1222 /// Clears all pending IPFIX flows.
1223 pub fn clear_ipfix_pending_flows(&mut self) {
1224 self.ipfix_parser.clear_pending_flows();
1225 }
1226
1227 /// Triggers template event hooks.
1228 ///
1229 /// This method is called internally by template operations to notify
1230 /// registered hooks about template lifecycle events. It can also be called
1231 /// manually for testing or custom integration scenarios.
1232 ///
1233 /// # Arguments
1234 ///
1235 /// * `event` - The template event to trigger
1236 ///
1237 /// # Examples
1238 ///
1239 /// ```rust
1240 /// use netflow_parser::{NetflowParser, TemplateEvent, TemplateProtocol};
1241 ///
1242 /// let parser = NetflowParser::default();
1243 /// parser.trigger_template_event(TemplateEvent::Learned {
1244 /// template_id: 256,
1245 /// protocol: TemplateProtocol::V9,
1246 /// });
1247 /// ```
1248 #[inline]
1249 pub fn trigger_template_event(&self, event: TemplateEvent) {
1250 self.template_hooks.trigger(&event);
1251 }
1252
1253 /// Parses NetFlow packets from a byte slice, preserving all successfully parsed packets.
1254 ///
1255 /// This function parses packets in sequence and returns a [`ParseResult`] containing both
1256 /// successfully parsed packets and an optional error. **No data is lost** - if parsing fails
1257 /// partway through, you still get all packets parsed before the error.
1258 ///
1259 /// # Arguments
1260 ///
1261 /// * `packet` - Byte slice containing NetFlow packet(s)
1262 ///
1263 /// # Returns
1264 ///
1265 /// [`ParseResult`] with:
1266 /// * `packets` - All successfully parsed packets (even if error occurred)
1267 /// * `error` - `None` if fully successful, `Some(error)` if parsing stopped
1268 ///
1269 /// # Examples
1270 ///
1271 /// ## Basic usage
1272 ///
1273 /// ```rust
1274 /// use netflow_parser::NetflowParser;
1275 ///
1276 /// let v5_packet = [0, 5, 2, 0, 3, 0, 4, 0, 5, 0, 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, 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, 2, 3, 4, 5, 6, 7,];
1277 /// let result = NetflowParser::default().parse_bytes(&v5_packet);
1278 ///
1279 /// // Process all packets
1280 /// for packet in result.packets {
1281 /// println!("Parsed packet");
1282 /// }
1283 ///
1284 /// // Check for errors
1285 /// if let Some(e) = result.error {
1286 /// eprintln!("Error: {}", e);
1287 /// }
1288 /// ```
1289 ///
1290 /// ## With JSON serialization
1291 ///
1292 /// ```rust
1293 /// use serde_json::json;
1294 /// use netflow_parser::NetflowParser;
1295 ///
1296 /// let v5_packet = [0, 5, 2, 0, 3, 0, 4, 0, 5, 0, 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, 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, 2, 3, 4, 5, 6, 7,];
1297 /// let result = NetflowParser::default().parse_bytes(&v5_packet);
1298 /// println!("{}", json!(result.packets).to_string());
1299 /// ```
1300 ///
1301 #[inline]
1302 pub fn parse_bytes(&mut self, packet: &[u8]) -> ParseResult {
1303 if packet.is_empty() {
1304 return ParseResult {
1305 packets: vec![],
1306 error: None,
1307 };
1308 }
1309
1310 let mut packets = Vec::new();
1311 let mut remaining = packet;
1312 let mut error = None;
1313
1314 while !remaining.is_empty() {
1315 match self.parse_packet_by_version(remaining) {
1316 ParsedNetflow::Success {
1317 packet,
1318 remaining: new_remaining,
1319 } => {
1320 packets.push(packet);
1321 remaining = new_remaining;
1322 }
1323 ParsedNetflow::UnallowedVersion => {
1324 break;
1325 }
1326 ParsedNetflow::Error { error: e } => {
1327 // Store error but keep successfully parsed packets
1328 error = Some(e);
1329 break;
1330 }
1331 }
1332 }
1333
1334 ParseResult { packets, error }
1335 }
1336
1337 /// Returns an iterator that yields NetflowPacket items without allocating a Vec.
1338 /// This is useful for processing large batches of packets without collecting all results in memory.
1339 ///
1340 /// # Examples
1341 ///
1342 /// ```rust
1343 /// use netflow_parser::{NetflowParser, NetflowPacket};
1344 ///
1345 /// let v5_packet = [0, 5, 0, 1, 3, 0, 4, 0, 5, 0, 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, 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, 2, 3, 4, 5, 6, 7,];
1346 /// let mut parser = NetflowParser::default();
1347 ///
1348 /// for result in parser.iter_packets(&v5_packet) {
1349 /// match result {
1350 /// Ok(NetflowPacket::V5(v5)) => println!("V5 packet: {:?}", v5.header.version),
1351 /// Err(e) => println!("Error: {:?}", e),
1352 /// _ => (),
1353 /// }
1354 /// }
1355 /// ```
1356 #[inline]
1357 pub fn iter_packets<'a>(&'a mut self, packet: &'a [u8]) -> NetflowPacketIterator<'a> {
1358 NetflowPacketIterator {
1359 parser: self,
1360 remaining: packet,
1361 errored: false,
1362 }
1363 }
1364
1365 #[inline]
1366 fn parse_packet_by_version<'a>(&mut self, packet: &'a [u8]) -> ParsedNetflow<'a> {
1367 match GenericNetflowHeader::parse(packet) {
1368 Ok((remaining, header)) if self.allowed_versions.contains(&header.version) => {
1369 match header.version {
1370 5 => V5Parser::parse(remaining),
1371 7 => V7Parser::parse(remaining),
1372 9 => self.v9_parser.parse(remaining),
1373 10 => self.ipfix_parser.parse(remaining),
1374 _ => ParsedNetflow::Error {
1375 error: NetflowError::UnsupportedVersion {
1376 version: header.version,
1377 offset: 0,
1378 sample: packet[..packet.len().min(self.max_error_sample_size)]
1379 .to_vec(),
1380 },
1381 },
1382 }
1383 }
1384 Ok(_) => {
1385 // Version is valid but filtered by allowed_versions
1386 ParsedNetflow::UnallowedVersion
1387 }
1388 Err(e) => ParsedNetflow::Error {
1389 error: NetflowError::Incomplete {
1390 available: packet.len(),
1391 context: format!("NetFlow header: {}", e),
1392 },
1393 },
1394 }
1395 }
1396
1397 /// Takes a Netflow packet slice and returns a vector of Parsed NetflowCommonFlowSet
1398 #[cfg(feature = "netflow_common")]
1399 #[inline]
1400 pub fn parse_bytes_as_netflow_common_flowsets(
1401 &mut self,
1402 packet: &[u8],
1403 ) -> Vec<NetflowCommonFlowSet> {
1404 let netflow_packets = self.parse_bytes(packet);
1405 netflow_packets
1406 .packets
1407 .iter()
1408 .flat_map(|n| n.as_netflow_common().unwrap_or_default().flowsets)
1409 .collect()
1410 }
1411}