1use super::error::{ParseError, Result};
45use super::value::{decode_record, DecodeContext};
46use super::Cursor;
47use crate::models::*;
48use crate::registry::{MIN_DATA_SET_ID, OPTIONS_TEMPLATE_SET_ID, TEMPLATE_SET_ID};
49use std::collections::HashMap;
50
51const MESSAGE_HEADER_LENGTH: usize = 16;
53
54const SET_HEADER_LENGTH: usize = 4;
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
59pub enum Transport {
60 #[default]
63 Udp,
64 Tcp,
66 Sctp,
68 File,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct DecoderConfig {
76 pub transport: Transport,
80 pub template_lifetime: Option<u32>,
84 pub max_templates: usize,
89 pub max_list_depth: usize,
92}
93
94impl Default for DecoderConfig {
95 fn default() -> Self {
96 Self {
97 transport: Transport::Udp,
98 template_lifetime: None,
99 max_templates: 4096,
100 max_list_depth: 8,
101 }
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
107pub(crate) struct Template {
108 pub(crate) field_specifiers: Vec<FieldSpecifier>,
109 pub(crate) scope_field_count: u16,
111 pub(crate) min_record_length: usize,
114 pub(crate) last_received: u32,
116}
117
118impl Template {
119 fn new(field_specifiers: Vec<FieldSpecifier>, scope_field_count: u16) -> Result<Self> {
120 let min_record_length = field_specifiers
121 .iter()
122 .map(|f| {
123 if f.is_variable_length() {
124 1
125 } else {
126 f.field_length as usize
127 }
128 })
129 .sum();
130 if min_record_length == 0 {
131 return Err(ParseError::InvalidData(
132 "Template describes zero-length Data Records".to_string(),
133 ));
134 }
135 Ok(Self {
136 field_specifiers,
137 scope_field_count,
138 min_record_length,
139 last_received: 0,
140 })
141 }
142
143 fn is_options(&self) -> bool {
144 self.scope_field_count > 0
145 }
146}
147
148pub(crate) type TemplateCache = HashMap<(u32, u16), Template>;
149
150#[derive(Debug, Clone, Default)]
152pub struct Decoder {
153 config: DecoderConfig,
154 templates: TemplateCache,
155 last_sweep: Option<u32>,
158}
159
160impl Decoder {
161 pub fn new() -> Self {
163 Self::default()
164 }
165
166 pub fn with_config(config: DecoderConfig) -> Self {
168 Self {
169 config,
170 templates: HashMap::new(),
171 last_sweep: None,
172 }
173 }
174
175 pub fn template_count(&self) -> usize {
177 self.templates.len()
178 }
179
180 pub fn has_template(&self, observation_domain_id: u32, template_id: u16) -> bool {
183 self.templates
184 .contains_key(&(observation_domain_id, template_id))
185 }
186
187 pub fn template_last_received(
190 &self,
191 observation_domain_id: u32,
192 template_id: u16,
193 ) -> Option<u32> {
194 self.templates
195 .get(&(observation_domain_id, template_id))
196 .map(|t| t.last_received)
197 }
198
199 pub fn expire_templates(&mut self, now: u32) {
206 if let Some(lifetime) = self.config.template_lifetime {
207 self.templates
208 .retain(|_, t| now.saturating_sub(t.last_received) <= lifetime);
209 }
210 }
211
212 pub fn clear(&mut self) {
214 self.templates.clear();
215 }
216
217 pub fn remove_observation_domain(&mut self, observation_domain_id: u32) {
219 self.templates
220 .retain(|(domain, _), _| *domain != observation_domain_id);
221 }
222
223 pub fn decode(&mut self, data: &[u8]) -> Result<Message> {
228 let mut cursor = Cursor::new(data);
229 let header = parse_message_header(&mut cursor)?;
230 let body = cursor.read_bytes(
231 header.length as usize - MESSAGE_HEADER_LENGTH,
232 "message body",
233 )?;
234
235 let now = header.export_time;
236 if self.config.transport == Transport::Udp && self.last_sweep != Some(now) {
237 self.expire_templates(now);
238 self.last_sweep = Some(now);
239 }
240
241 let mut sets = Vec::new();
242 let mut cursor = Cursor::new(body);
243 while !cursor.is_empty() {
244 sets.push(self.parse_set(&mut cursor, header.observation_domain_id, now)?);
245 }
246
247 Ok(Message { header, sets })
248 }
249
250 pub fn decode_all(&mut self, data: &[u8]) -> Result<Vec<Message>> {
253 let mut messages = Vec::new();
254 let mut offset = 0;
255 while offset < data.len() {
256 let message = self.decode(&data[offset..])?;
257 offset += message.header.length as usize;
258 messages.push(message);
259 }
260 Ok(messages)
261 }
262
263 fn parse_set(&mut self, cursor: &mut Cursor, domain: u32, now: u32) -> Result<Set> {
264 let header = SetHeader {
265 set_id: cursor.read_u16("set header")?,
266 length: cursor.read_u16("set header")?,
267 };
268 if (header.length as usize) < SET_HEADER_LENGTH {
269 return Err(ParseError::InvalidData(format!(
270 "Set length {} is smaller than the Set Header",
271 header.length
272 )));
273 }
274 let content = cursor.read_bytes(header.length as usize - SET_HEADER_LENGTH, "set")?;
275 let mut content = Cursor::new(content);
276
277 match header.set_id {
278 TEMPLATE_SET_ID => Ok(Set::Template {
279 header,
280 records: self.parse_template_set(&mut content, domain, now)?,
281 }),
282 OPTIONS_TEMPLATE_SET_ID => Ok(Set::OptionsTemplate {
283 header,
284 records: self.parse_options_template_set(&mut content, domain, now)?,
285 }),
286 id if id >= MIN_DATA_SET_ID => match self.templates.get(&(domain, id)) {
287 Some(template) => {
288 let ctx = DecodeContext {
289 templates: &self.templates,
290 observation_domain_id: domain,
291 max_depth: self.config.max_list_depth,
292 };
293 let mut records = Vec::new();
294 while content.remaining() >= template.min_record_length {
297 records.push(decode_record(&ctx, id, template, &mut content, 0)?);
298 }
299 Ok(Set::Data { header, records })
300 }
301 None => Ok(Set::UndecodedData {
302 header,
303 payload: content.read_rest().to_vec(),
304 }),
305 },
306 _ => Ok(Set::Reserved {
307 header,
308 payload: content.read_rest().to_vec(),
309 }),
310 }
311 }
312
313 fn parse_template_set(
315 &mut self,
316 cursor: &mut Cursor,
317 domain: u32,
318 now: u32,
319 ) -> Result<Vec<TemplateRecord>> {
320 let mut records = Vec::new();
321 while cursor.remaining() >= 4 {
323 let template_id = cursor.read_u16("template record header")?;
324 let field_count = cursor.read_u16("template record header")?;
325
326 if field_count == 0 {
327 self.withdraw(domain, template_id, false)?;
328 records.push(TemplateRecord {
329 template_id,
330 field_count,
331 field_specifiers: Vec::new(),
332 });
333 continue;
334 }
335
336 check_template_id(template_id)?;
337 let field_specifiers = parse_field_specifiers(cursor, field_count)?;
338 self.insert(
339 domain,
340 template_id,
341 Template::new(field_specifiers.clone(), 0)?,
342 now,
343 )?;
344 records.push(TemplateRecord {
345 template_id,
346 field_count,
347 field_specifiers,
348 });
349 }
350 Ok(records)
351 }
352
353 fn parse_options_template_set(
356 &mut self,
357 cursor: &mut Cursor,
358 domain: u32,
359 now: u32,
360 ) -> Result<Vec<OptionsTemplateRecord>> {
361 let mut records = Vec::new();
362 while cursor.remaining() >= 4 {
364 let template_id = cursor.read_u16("options template record header")?;
365 let field_count = cursor.read_u16("options template record header")?;
366
367 if field_count == 0 {
368 self.withdraw(domain, template_id, true)?;
369 records.push(OptionsTemplateRecord {
370 template_id,
371 field_count,
372 scope_field_count: 0,
373 field_specifiers: Vec::new(),
374 });
375 continue;
376 }
377
378 check_template_id(template_id)?;
379 let scope_field_count = cursor.read_u16("options template record header")?;
380 if scope_field_count == 0 || scope_field_count > field_count {
381 return Err(ParseError::InvalidData(format!(
382 "Invalid Scope Field Count {} for Field Count {}",
383 scope_field_count, field_count
384 )));
385 }
386 let field_specifiers = parse_field_specifiers(cursor, field_count)?;
387 self.insert(
388 domain,
389 template_id,
390 Template::new(field_specifiers.clone(), scope_field_count)?,
391 now,
392 )?;
393 records.push(OptionsTemplateRecord {
394 template_id,
395 field_count,
396 scope_field_count,
397 field_specifiers,
398 });
399 }
400 Ok(records)
401 }
402
403 fn insert(
404 &mut self,
405 domain: u32,
406 template_id: u16,
407 mut template: Template,
408 now: u32,
409 ) -> Result<()> {
410 template.last_received = now;
411 let key = (domain, template_id);
412 if !self.templates.contains_key(&key) && self.templates.len() >= self.config.max_templates {
413 return Err(ParseError::TemplateLimitExceeded(self.config.max_templates));
414 }
415 self.templates.insert(key, template);
416 Ok(())
417 }
418
419 fn withdraw(&mut self, domain: u32, template_id: u16, options: bool) -> Result<()> {
422 if self.config.transport == Transport::Udp {
423 return Ok(());
424 }
425 let withdraw_all_id = if options {
426 OPTIONS_TEMPLATE_SET_ID
427 } else {
428 TEMPLATE_SET_ID
429 };
430 if template_id == withdraw_all_id {
431 self.templates
433 .retain(|(d, _), t| *d != domain || t.is_options() != options);
434 Ok(())
435 } else if template_id >= MIN_DATA_SET_ID {
436 self.templates.remove(&(domain, template_id));
437 Ok(())
438 } else {
439 Err(ParseError::InvalidData(format!(
440 "Invalid Template ID {} in Template Withdrawal",
441 template_id
442 )))
443 }
444 }
445}
446
447fn parse_message_header(cursor: &mut Cursor) -> Result<MessageHeader> {
449 let available = cursor.remaining();
450 let header = MessageHeader {
451 version: cursor.read_u16("message header")?,
452 length: cursor.read_u16("message header")?,
453 export_time: cursor.read_u32("message header")?,
454 sequence_number: cursor.read_u32("message header")?,
455 observation_domain_id: cursor.read_u32("message header")?,
456 };
457 if header.version != crate::registry::IPFIX_VERSION {
458 return Err(ParseError::InvalidVersion(header.version));
459 }
460 if (header.length as usize) < MESSAGE_HEADER_LENGTH {
461 return Err(ParseError::InvalidData(format!(
462 "Message length {} is smaller than the Message Header",
463 header.length
464 )));
465 }
466 if header.length as usize > available {
467 return Err(ParseError::Truncated {
468 context: "message",
469 needed: header.length as usize,
470 available,
471 });
472 }
473 Ok(header)
474}
475
476fn check_template_id(template_id: u16) -> Result<()> {
477 if template_id < MIN_DATA_SET_ID {
478 return Err(ParseError::InvalidData(format!(
479 "Invalid Template ID {} (must be > 255)",
480 template_id
481 )));
482 }
483 Ok(())
484}
485
486fn parse_field_specifiers(cursor: &mut Cursor, field_count: u16) -> Result<Vec<FieldSpecifier>> {
488 let mut field_specifiers =
490 Vec::with_capacity((field_count as usize).min(cursor.remaining() / 4));
491 for _ in 0..field_count {
492 let (information_element_identifier, enterprise) =
493 cursor.read_field_id("field specifier")?;
494 let field_length = cursor.read_u16("field specifier")?;
495 let enterprise_number = if enterprise {
496 Some(cursor.read_u32("field specifier")?)
497 } else {
498 None
499 };
500 field_specifiers.push(FieldSpecifier {
501 information_element_identifier,
502 field_length,
503 enterprise_number,
504 });
505 }
506 Ok(field_specifiers)
507}
508
509pub fn parse_message(data: &[u8]) -> Result<Message> {
518 Decoder::new().decode(data)
519}
520
521pub fn parse_messages(data: &[u8]) -> Result<Vec<Message>> {
529 Decoder::with_config(DecoderConfig {
530 transport: Transport::File,
531 ..DecoderConfig::default()
532 })
533 .decode_all(data)
534}