Skip to main content

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}