1use anyhow::{bail, ensure, Context, Result};
3use serde::{Deserialize, Serialize};
4use std::{
5 collections::BTreeMap,
6 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
7 time::Duration,
8};
9use tokio::time::Instant;
10
11pub const MAX_MESSAGE_BYTES: usize = 8192;
12pub const MAX_SAMPLES: usize = 32;
13pub const MAX_RECORDS_PER_SAMPLE: usize = 64;
14pub const MAX_RECORDS: usize = 256;
15pub const MAX_HEADER_BYTES: usize = 256;
16pub const MAX_OPAQUE_BYTES: usize = 4096;
17pub const MAX_SESSIONS: usize = 128;
18pub const MAX_IPV6_EXTENSIONS: usize = 8;
19pub const DEFAULT_SESSION_IDLE: u64 = 1800;
20pub const DEFAULT_LLM_FALLBACK: bool = false;
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
23#[serde(deny_unknown_fields)]
24pub struct Source {
25 pub class: u32,
26 pub index: u32,
27}
28#[derive(Clone, Debug, Serialize, Deserialize)]
29#[serde(deny_unknown_fields)]
30pub struct Interface {
31 pub format: u32,
32 pub value: u32,
33}
34#[derive(Clone, Debug, Serialize, Deserialize)]
35#[serde(deny_unknown_fields)]
36pub struct Packet {
37 pub source_ip: IpAddr,
38 pub destination_ip: IpAddr,
39 pub packet_length: u32,
40 pub protocol: u8,
41 pub source_port: u16,
42 pub destination_port: u16,
43 pub tcp_flags: u32,
44 pub traffic_class: u8,
45}
46impl Packet {
47 fn validate(&self, ipv6: bool) -> Result<()> {
48 ensure!(
49 self.source_ip.is_ipv6() == ipv6 && self.destination_ip.is_ipv6() == ipv6,
50 "packet address families must match record"
51 );
52 ensure!(self.tcp_flags <= 0x1ff, "TCP flags limit9bits");
53 Ok(())
54 }
55}
56macro_rules! counters {
57 ($name:ident {$($field:ident:$ty:ident),* $(,)?}) => {
58 #[derive(Clone,Debug,Serialize,Deserialize)]
59 #[serde(deny_unknown_fields)]
60 pub struct $name { $(pub $field:$ty),* }
61 impl $name {
62 fn encode(&self,out:&mut Vec<u8>) { $(out.extend_from_slice(&self.$field.to_be_bytes());)* }
63 fn decode(r:&mut Reader<'_>)->Result<Self>{Ok(Self{$($field:r.$ty()?),*})}
64 }
65 };
66}
67counters!(InterfaceCounters {
68 if_index: u32,
69 if_type: u32,
70 if_speed: u64,
71 if_direction: u32,
72 if_status: u32,
73 in_octets: u64,
74 in_unicast_packets: u32,
75 in_multicast_packets: u32,
76 in_broadcast_packets: u32,
77 in_discards: u32,
78 in_errors: u32,
79 in_unknown_protocols: u32,
80 out_octets: u64,
81 out_unicast_packets: u32,
82 out_multicast_packets: u32,
83 out_broadcast_packets: u32,
84 out_discards: u32,
85 out_errors: u32,
86 promiscuous_mode: u32
87});
88counters!(EthernetCounters {
89 alignment_errors: u32,
90 fcs_errors: u32,
91 single_collision_frames: u32,
92 multiple_collision_frames: u32,
93 sqe_test_errors: u32,
94 deferred_transmissions: u32,
95 late_collisions: u32,
96 excessive_collisions: u32,
97 internal_mac_transmit_errors: u32,
98 carrier_sense_errors: u32,
99 frame_too_longs: u32,
100 internal_mac_receive_errors: u32,
101 symbol_errors: u32
102});
103counters!(VlanCounters {
104 vlan_id: u32,
105 octets: u64,
106 unicast_packets: u32,
107 multicast_packets: u32,
108 broadcast_packets: u32,
109 discards: u32
110});
111#[derive(Clone, Debug, Serialize, Deserialize)]
112#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
113pub enum CounterRecord {
114 Interface(InterfaceCounters),
115 Ethernet(EthernetCounters),
116 Vlan(VlanCounters),
117}
118#[derive(Clone, Debug, Serialize, Deserialize)]
119#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
120pub enum FlowRecord {
121 SynthesizedHeader {
122 packet: Packet,
123 frame_length: u32,
124 stripped: u32,
125 },
126 SampledIpv4(Packet),
127 SampledIpv6(Packet),
128 Ethernet {
129 frame_length: u32,
130 source_mac: String,
131 destination_mac: String,
132 ether_type: u32,
133 },
134 ExtendedSwitch {
135 source_vlan: u32,
136 source_priority: u32,
137 destination_vlan: u32,
138 destination_priority: u32,
139 },
140}
141#[derive(Clone, Debug, Serialize, Deserialize)]
142#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
143pub enum Sample {
144 Flow {
145 #[serde(default)]
146 expanded: bool,
147 sequence_number: u32,
148 source: Source,
149 sampling_rate: u32,
150 sample_pool: u32,
151 drops: u32,
152 input: Interface,
153 output: Interface,
154 records: Vec<FlowRecord>,
155 },
156 Counters {
157 #[serde(default)]
158 expanded: bool,
159 sequence_number: u32,
160 source: Source,
161 records: Vec<CounterRecord>,
162 },
163}
164#[derive(Clone, Debug, Serialize, Deserialize)]
165#[serde(deny_unknown_fields)]
166pub struct Batch {
167 pub agent_address: IpAddr,
168 pub sub_agent_id: u32,
169 #[serde(default)]
170 pub uptime_ms: Option<u32>,
171 pub samples: Vec<Sample>,
172}
173#[derive(Debug, Serialize)]
174pub struct LinkLayer {
175 pub source_mac: String,
176 pub destination_mac: String,
177 pub ether_type: u32,
178 pub vlans: Vec<u16>,
179}
180#[derive(Debug, Serialize)]
181#[serde(tag = "kind", rename_all = "snake_case")]
182pub enum Record {
183 SampledHeader {
184 header_protocol: u32,
185 frame_length: u32,
186 stripped: u32,
187 header_length: usize,
188 packet: Option<Packet>,
189 link_layer: Option<LinkLayer>,
190 decode_status: &'static str,
191 },
192 SampledIpv4(Packet),
193 SampledIpv6(Packet),
194 Ethernet {
195 frame_length: u32,
196 source_mac: String,
197 destination_mac: String,
198 ether_type: u32,
199 },
200 ExtendedSwitch {
201 source_vlan: u32,
202 source_priority: u32,
203 destination_vlan: u32,
204 destination_priority: u32,
205 },
206 InterfaceCounters(InterfaceCounters),
207 EthernetCounters(EthernetCounters),
208 VlanCounters(VlanCounters),
209 Unknown {
210 enterprise: u32,
211 format: u32,
212 byte_count: usize,
213 },
214}
215#[derive(Debug, Serialize)]
216pub struct FlowMeta {
217 pub sampling_rate: u32,
218 pub sample_pool: u32,
219 pub drops: u32,
220 pub input: Interface,
221 pub output: Interface,
222}
223#[derive(Debug, Serialize)]
224#[serde(tag = "kind", rename_all = "snake_case")]
225pub enum DecodedSample {
226 Flow {
227 expanded: bool,
228 sequence_number: u32,
229 source: Source,
230 flow: FlowMeta,
231 records: Vec<Record>,
232 },
233 Counters {
234 expanded: bool,
235 sequence_number: u32,
236 source: Source,
237 records: Vec<Record>,
238 },
239 Unknown {
240 enterprise: u32,
241 format: u32,
242 byte_count: usize,
243 },
244}
245#[derive(Debug, Serialize)]
246pub struct Message {
247 pub agent_address: IpAddr,
248 pub sub_agent_id: u32,
249 pub sequence_number: u32,
250 pub uptime_ms: u32,
251 pub samples: Vec<DecodedSample>,
252 pub record_count: usize,
253 pub sequence_tracking: SequenceTracking,
254}
255#[derive(Debug, Serialize)]
256pub struct SequenceTracking {
257 pub expected: Option<u32>,
258 pub status: &'static str,
259 pub missing_datagrams: Option<u32>,
260 pub uptime_decreased: bool,
261}
262struct Reader<'a> {
263 bytes: &'a [u8],
264 at: usize,
265}
266impl<'a> Reader<'a> {
267 fn new(bytes: &'a [u8]) -> Self {
268 Self { bytes, at: 0 }
269 }
270 fn left(&self) -> usize {
271 self.bytes.len() - self.at
272 }
273 fn take(&mut self, n: usize) -> Result<&'a [u8]> {
274 ensure!(n <= self.left(), "truncated sFlow XDR field");
275 let s = &self.bytes[self.at..self.at + n];
276 self.at += n;
277 Ok(s)
278 }
279 fn u32(&mut self) -> Result<u32> {
280 Ok(u32::from_be_bytes(self.take(4)?.try_into()?))
281 }
282 fn u64(&mut self) -> Result<u64> {
283 Ok(u64::from_be_bytes(self.take(8)?.try_into()?))
284 }
285 fn opaque(&mut self, limit: usize) -> Result<&'a [u8]> {
286 let n = self.u32()? as usize;
287 ensure!(n <= limit, "sFlow opaque byte bound");
288 let data = self.take(n)?;
289 self.take((4 - n % 4) % 4)?;
290 Ok(data)
291 }
292 fn done(&self) -> Result<()> {
293 ensure!(self.left() == 0, "unexpected trailing sFlow fields");
294 Ok(())
295 }
296}
297fn int(out: &mut Vec<u8>, n: u32) {
298 out.extend_from_slice(&n.to_be_bytes());
299}
300fn opaque(out: &mut Vec<u8>, tag: u32, data: &[u8]) -> Result<()> {
301 ensure!(
302 data.len() <= MAX_MESSAGE_BYTES,
303 "sFlow structure byte limit"
304 );
305 int(out, tag);
306 int(out, data.len() as u32);
307 out.extend_from_slice(data);
308 out.resize(out.len() + (4 - data.len() % 4) % 4, 0);
309 ensure!(
310 out.len() <= MAX_MESSAGE_BYTES,
311 "sFlow message byte limit8192"
312 );
313 Ok(())
314}
315fn source(out: &mut Vec<u8>, s: &Source, expanded: bool) -> Result<()> {
316 ensure!(s.class <= 2, "unsupported source class");
317 if expanded {
318 int(out, s.class);
319 int(out, s.index);
320 } else {
321 ensure!(s.index < 1 << 24, "compact source index limit24bits");
322 int(out, (s.class << 24) | s.index);
323 }
324 Ok(())
325}
326fn interface(out: &mut Vec<u8>, i: &Interface, expanded: bool) -> Result<()> {
327 ensure!(i.format <= 2, "unsupported interface format");
328 if expanded {
329 int(out, i.format);
330 int(out, i.value);
331 } else {
332 ensure!(i.value < 1 << 30, "compact interface value limit30bits");
333 int(out, (i.format << 30) | i.value);
334 }
335 Ok(())
336}
337fn read_source(r: &mut Reader<'_>, expanded: bool) -> Result<Source> {
338 let n = r.u32()?;
339 Ok(if expanded {
340 Source {
341 class: n,
342 index: r.u32()?,
343 }
344 } else {
345 Source {
346 class: n >> 24,
347 index: n & 0xffffff,
348 }
349 })
350}
351fn read_interface(r: &mut Reader<'_>, expanded: bool) -> Result<Interface> {
352 let n = r.u32()?;
353 Ok(if expanded {
354 Interface {
355 format: n,
356 value: r.u32()?,
357 }
358 } else {
359 Interface {
360 format: n >> 30,
361 value: n & 0x3fffffff,
362 }
363 })
364}
365pub fn encode(batch: &Batch, sequence: u32, uptime: u32) -> Result<(Vec<u8>, usize)> {
366 ensure!(
367 (1..=MAX_SAMPLES).contains(&batch.samples.len()),
368 "sample count must be1..32"
369 );
370 let mut out = Vec::new();
371 int(&mut out, 5);
372 match batch.agent_address {
373 IpAddr::V4(ip) => {
374 int(&mut out, 1);
375 out.extend_from_slice(&ip.octets());
376 }
377 IpAddr::V6(ip) => {
378 int(&mut out, 2);
379 out.extend_from_slice(&ip.octets());
380 }
381 }
382 int(&mut out, batch.sub_agent_id);
383 int(&mut out, sequence);
384 int(&mut out, uptime);
385 int(&mut out, batch.samples.len() as u32);
386 let mut count = 0;
387 for sample in &batch.samples {
388 let mut body = Vec::new();
389 let (tag, n) = match sample {
390 Sample::Flow {
391 expanded,
392 sequence_number,
393 source: s,
394 sampling_rate,
395 sample_pool,
396 drops,
397 input,
398 output,
399 records,
400 } => {
401 ensure!(
402 (1..=MAX_RECORDS_PER_SAMPLE).contains(&records.len()),
403 "flow record count1..64"
404 );
405 int(&mut body, *sequence_number);
406 source(&mut body, s, *expanded)?;
407 int(&mut body, *sampling_rate);
408 int(&mut body, *sample_pool);
409 int(&mut body, *drops);
410 interface(&mut body, input, *expanded)?;
411 interface(&mut body, output, *expanded)?;
412 int(&mut body, records.len() as u32);
413 for record in records {
414 let (t, b) = encode_flow(record)?;
415 opaque(&mut body, t, &b)?;
416 }
417 (if *expanded { 3 } else { 1 }, records.len())
418 }
419 Sample::Counters {
420 expanded,
421 sequence_number,
422 source: s,
423 records,
424 } => {
425 ensure!(
426 (1..=MAX_RECORDS_PER_SAMPLE).contains(&records.len()),
427 "counter record count1..64"
428 );
429 int(&mut body, *sequence_number);
430 source(&mut body, s, *expanded)?;
431 int(&mut body, records.len() as u32);
432 for record in records {
433 let mut b = Vec::new();
434 let t = match record {
435 CounterRecord::Interface(c) => {
436 ensure!(
437 c.if_direction <= 4 && c.if_status <= 3 && c.promiscuous_mode <= 2,
438 "interface counter flags"
439 );
440 c.encode(&mut b);
441 1
442 }
443 CounterRecord::Ethernet(c) => {
444 c.encode(&mut b);
445 2
446 }
447 CounterRecord::Vlan(c) => {
448 ensure!(c.vlan_id <= 4095, "VLAN identifier limit4095");
449 c.encode(&mut b);
450 5
451 }
452 };
453 opaque(&mut body, t, &b)?;
454 }
455 (if *expanded { 4 } else { 2 }, records.len())
456 }
457 };
458 count += n;
459 ensure!(count <= MAX_RECORDS, "total record count limit256");
460 opaque(&mut out, tag, &body)?;
461 }
462 Ok((out, count))
463}
464fn mac(text: &str) -> Result<[u8; 6]> {
465 ensure!(text.len() == 17, "MAC address must contain six octets");
466 let mut out = [0; 6];
467 let parts = text.split(':').collect::<Vec<_>>();
468 ensure!(parts.len() == 6, "MAC address delimiter");
469 for (i, p) in parts.iter().enumerate() {
470 ensure!(p.len() == 2, "MAC octet width");
471 out[i] = u8::from_str_radix(p, 16)?;
472 }
473 Ok(out)
474}
475fn mac_text(b: &[u8]) -> String {
476 b.iter()
477 .map(|n| format!("{n:02x}"))
478 .collect::<Vec<_>>()
479 .join(":")
480}
481fn encode_flow(record: &FlowRecord) -> Result<(u32, Vec<u8>)> {
482 let mut out = Vec::new();
483 let tag = match record {
484 FlowRecord::SynthesizedHeader {
485 packet,
486 frame_length,
487 stripped,
488 } => {
489 let ipv6 = packet.source_ip.is_ipv6();
490 packet.validate(ipv6)?;
491 ensure!(
492 *frame_length >= *stripped,
493 "stripped exceeds original frame"
494 );
495 let b = synthesized_header(packet)?;
496 ensure!(
497 usize::try_from(frame_length - stripped)? >= b.len(),
498 "captured header exceeds remaining frame"
499 );
500 int(&mut out, if ipv6 { 12 } else { 11 });
501 int(&mut out, *frame_length);
502 int(&mut out, *stripped);
503 int(&mut out, b.len() as u32);
504 out.extend_from_slice(&b);
505 out.resize(out.len() + (4 - b.len() % 4) % 4, 0);
506 1
507 }
508 FlowRecord::SampledIpv4(p) | FlowRecord::SampledIpv6(p) => {
509 let ipv6 = matches!(record, FlowRecord::SampledIpv6(_));
510 p.validate(ipv6)?;
511 int(&mut out, p.packet_length);
512 int(&mut out, u32::from(p.protocol));
513 match (p.source_ip, p.destination_ip) {
514 (IpAddr::V4(a), IpAddr::V4(b)) => {
515 out.extend_from_slice(&a.octets());
516 out.extend_from_slice(&b.octets());
517 }
518 (IpAddr::V6(a), IpAddr::V6(b)) => {
519 out.extend_from_slice(&a.octets());
520 out.extend_from_slice(&b.octets());
521 }
522 _ => bail!("packet families differ"),
523 };
524 int(&mut out, u32::from(p.source_port));
525 int(&mut out, u32::from(p.destination_port));
526 int(&mut out, p.tcp_flags);
527 int(&mut out, u32::from(p.traffic_class));
528 if ipv6 {
529 4
530 } else {
531 3
532 }
533 }
534 FlowRecord::Ethernet {
535 frame_length,
536 source_mac,
537 destination_mac,
538 ether_type,
539 } => {
540 ensure!(*ether_type <= 65535, "EtherType width");
541 int(&mut out, *frame_length);
542 out.extend_from_slice(&mac(source_mac)?);
543 out.extend_from_slice(&[0, 0]);
544 out.extend_from_slice(&mac(destination_mac)?);
545 out.extend_from_slice(&[0, 0]);
546 int(&mut out, *ether_type);
547 2
548 }
549 FlowRecord::ExtendedSwitch {
550 source_vlan,
551 source_priority,
552 destination_vlan,
553 destination_priority,
554 } => {
555 for v in [*source_vlan, *destination_vlan] {
556 ensure!(v <= 4095 || v == u32::MAX, "VLAN value");
557 }
558 for p in [*source_priority, *destination_priority] {
559 ensure!(p <= 7 || p == u32::MAX, "VLAN priority");
560 }
561 for n in [
562 source_vlan,
563 source_priority,
564 destination_vlan,
565 destination_priority,
566 ] {
567 int(&mut out, *n);
568 }
569 1001
570 }
571 };
572 Ok((tag, out))
573}
574fn synthesized_header(p: &Packet) -> Result<Vec<u8>> {
575 ensure!(
576 p.protocol == 6 || p.protocol == 17,
577 "synthesized headers support TCP/UDP only"
578 );
579 let ipv6 = p.source_ip.is_ipv6();
580 let ip_len = if ipv6 { 40 } else { 20 };
581 let transport_len = if p.protocol == 6 { 20 } else { 8 };
582 ensure!(
583 p.packet_length >= ip_len + transport_len && p.packet_length - ip_len <= 65535,
584 "IP packet length"
585 );
586 let mut b = vec![0; ip_len as usize];
587 match (p.source_ip, p.destination_ip) {
588 (IpAddr::V4(a), IpAddr::V4(c)) => {
589 b[0] = 0x45;
590 b[1] = p.traffic_class;
591 b[2..4].copy_from_slice(&u16::try_from(p.packet_length)?.to_be_bytes());
592 b[8] = 64;
593 b[9] = p.protocol;
594 b[12..16].copy_from_slice(&a.octets());
595 b[16..20].copy_from_slice(&c.octets());
596 let checksum = ipv4_checksum(&b);
597 b[10..12].copy_from_slice(&checksum.to_be_bytes());
598 }
599 (IpAddr::V6(a), IpAddr::V6(c)) => {
600 b[0] = 0x60 | (p.traffic_class >> 4);
601 b[1] = p.traffic_class << 4;
602 b[4..6].copy_from_slice(&((p.packet_length - 40) as u16).to_be_bytes());
603 b[6] = p.protocol;
604 b[7] = 64;
605 b[8..24].copy_from_slice(&a.octets());
606 b[24..40].copy_from_slice(&c.octets());
607 }
608 _ => bail!("synthesized packet family mismatch"),
609 }
610 let at = b.len();
611 b.resize(at + transport_len as usize, 0);
612 b[at..at + 2].copy_from_slice(&p.source_port.to_be_bytes());
613 b[at + 2..at + 4].copy_from_slice(&p.destination_port.to_be_bytes());
614 if p.protocol == 6 {
615 b[at + 12] = 0x50 | ((p.tcp_flags >> 8) as u8);
616 b[at + 13] = p.tcp_flags as u8;
617 } else {
618 b[at + 4..at + 6].copy_from_slice(&((p.packet_length - ip_len) as u16).to_be_bytes());
619 }
620 Ok(b)
623}
624fn ipv4_checksum(b: &[u8]) -> u16 {
625 let mut sum = b
626 .chunks_exact(2)
627 .map(|w| u32::from(u16::from_be_bytes([w[0], w[1]])))
628 .sum::<u32>();
629 while sum >> 16 != 0 {
630 sum = (sum & 65535) + (sum >> 16);
631 }
632 !(sum as u16)
633}
634pub fn decode(bytes: &[u8]) -> Result<Message> {
635 ensure!(
636 bytes.len() <= MAX_MESSAGE_BYTES,
637 "sFlow message byte limit8192"
638 );
639 let mut r = Reader::new(bytes);
640 ensure!(r.u32()? == 5, "sFlow version5 required");
641 let agent_address = match r.u32()? {
642 1 => IpAddr::V4(Ipv4Addr::from(<[u8; 4]>::try_from(r.take(4)?)?)),
643 2 => IpAddr::V6(Ipv6Addr::from(<[u8; 16]>::try_from(r.take(16)?)?)),
644 _ => bail!("unsupported agent address type"),
645 };
646 let sub_agent_id = r.u32()?;
647 let sequence_number = r.u32()?;
648 let uptime_ms = r.u32()?;
649 let count = r.u32()? as usize;
650 ensure!(count <= MAX_SAMPLES, "sample count limit32");
651 let mut samples = Vec::with_capacity(count);
652 let mut record_count = 0;
653 for _ in 0..count {
654 let tag = r.u32()?;
655 let data = r.opaque(MAX_MESSAGE_BYTES)?;
656 let enterprise = tag >> 12;
657 let format = tag & 4095;
658 if enterprise != 0 || !(1..=4).contains(&format) {
659 ensure!(
660 data.len() <= MAX_OPAQUE_BYTES,
661 "unknown sample byte limit4096"
662 );
663 samples.push(DecodedSample::Unknown {
664 enterprise,
665 format,
666 byte_count: data.len(),
667 });
668 continue;
669 }
670 let flow = format == 1 || format == 3;
671 let expanded = format >= 3;
672 let mut s = Reader::new(data);
673 let sequence_number = s.u32()?;
674 let source = read_source(&mut s, expanded)?;
675 let flow_meta = if flow {
676 Some(FlowMeta {
677 sampling_rate: s.u32()?,
678 sample_pool: s.u32()?,
679 drops: s.u32()?,
680 input: read_interface(&mut s, expanded)?,
681 output: read_interface(&mut s, expanded)?,
682 })
683 } else {
684 None
685 };
686 let n = s.u32()? as usize;
687 ensure!(n <= MAX_RECORDS_PER_SAMPLE, "records/sample limit64");
688 record_count += n;
689 ensure!(record_count <= MAX_RECORDS, "total record limit256");
690 let mut records = Vec::with_capacity(n);
691 for _ in 0..n {
692 let tag = s.u32()?;
693 let b = s.opaque(MAX_OPAQUE_BYTES)?;
694 records.push(decode_record(tag, b, flow)?);
695 }
696 s.done()?;
697 samples.push(if let Some(flow) = flow_meta {
698 DecodedSample::Flow {
699 expanded,
700 sequence_number,
701 source,
702 flow,
703 records,
704 }
705 } else {
706 DecodedSample::Counters {
707 expanded,
708 sequence_number,
709 source,
710 records,
711 }
712 });
713 }
714 r.done()?;
715 Ok(Message {
716 agent_address,
717 sub_agent_id,
718 sequence_number,
719 uptime_ms,
720 samples,
721 record_count,
722 sequence_tracking: SequenceTracking {
723 expected: None,
724 status: "untracked",
725 missing_datagrams: None,
726 uptime_decreased: false,
727 },
728 })
729}
730fn decode_record(tag: u32, bytes: &[u8], flow: bool) -> Result<Record> {
731 let enterprise = tag >> 12;
732 let format = tag & 4095;
733 let mut r = Reader::new(bytes);
734 if enterprise != 0
735 || !if flow {
736 [1, 2, 3, 4, 1001].contains(&format)
737 } else {
738 [1, 2, 5].contains(&format)
739 }
740 {
741 return Ok(Record::Unknown {
742 enterprise,
743 format,
744 byte_count: bytes.len(),
745 });
746 }
747 let record = if flow {
748 match format {
749 1 => {
750 let header_protocol = r.u32()?;
751 let frame_length = r.u32()?;
752 let stripped = r.u32()?;
753 let b = r.opaque(MAX_HEADER_BYTES)?;
754 ensure!(
755 stripped <= frame_length
756 && b.len() <= usize::try_from(frame_length - stripped)?,
757 "sampled header/frame lengths"
758 );
759 let (packet, link_layer, decode_status) = header_summary(header_protocol, b);
760 Record::SampledHeader {
761 header_protocol,
762 frame_length,
763 stripped,
764 header_length: b.len(),
765 packet,
766 link_layer,
767 decode_status,
768 }
769 }
770 2 => {
771 let frame_length = r.u32()?;
772 let source_mac = mac_text(&r.take(8)?[..6]);
773 let destination_mac = mac_text(&r.take(8)?[..6]);
774 let ether_type = r.u32()?;
775 Record::Ethernet {
776 frame_length,
777 source_mac,
778 destination_mac,
779 ether_type,
780 }
781 }
782 3 | 4 => {
783 let packet_length = r.u32()?;
784 let protocol = u8::try_from(r.u32()?).context("IP protocol width")?;
785 let ipv6 = format == 4;
786 let mut addr = || -> Result<IpAddr> {
787 Ok(if ipv6 {
788 IpAddr::V6(Ipv6Addr::from(<[u8; 16]>::try_from(r.take(16)?)?))
789 } else {
790 IpAddr::V4(Ipv4Addr::from(<[u8; 4]>::try_from(r.take(4)?)?))
791 })
792 };
793 let source_ip = addr()?;
794 let destination_ip = addr()?;
795 let p = Packet {
796 source_ip,
797 destination_ip,
798 packet_length,
799 protocol,
800 source_port: u16::try_from(r.u32()?)?,
801 destination_port: u16::try_from(r.u32()?)?,
802 tcp_flags: r.u32()?,
803 traffic_class: u8::try_from(r.u32()?)?,
804 };
805 if ipv6 {
806 Record::SampledIpv6(p)
807 } else {
808 Record::SampledIpv4(p)
809 }
810 }
811 1001 => Record::ExtendedSwitch {
812 source_vlan: r.u32()?,
813 source_priority: r.u32()?,
814 destination_vlan: r.u32()?,
815 destination_priority: r.u32()?,
816 },
817 _ => unreachable!(),
818 }
819 } else {
820 match format {
821 1 => Record::InterfaceCounters(InterfaceCounters::decode(&mut r)?),
822 2 => Record::EthernetCounters(EthernetCounters::decode(&mut r)?),
823 5 => Record::VlanCounters(VlanCounters::decode(&mut r)?),
824 _ => unreachable!(),
825 }
826 };
827 r.done()?;
828 Ok(record)
829}
830fn header_summary(
831 protocol: u32,
832 bytes: &[u8],
833) -> (Option<Packet>, Option<LinkLayer>, &'static str) {
834 let mut at = 0;
835 let mut link = None;
836 let ip_protocol = if protocol == 1 {
837 if bytes.len() < 14 {
838 return (None, None, "truncated");
839 }
840 let mut ether_type = u16::from_be_bytes([bytes[12], bytes[13]]);
841 at = 14;
842 let mut vlans = vec![];
843 while ether_type == 0x8100 || ether_type == 0x88a8 {
844 if vlans.len() == 2 {
845 return (None, None, "vlan_depth_limit");
846 }
847 if bytes.len() < at + 4 {
848 return (None, None, "truncated");
849 }
850 vlans.push(u16::from_be_bytes([bytes[at], bytes[at + 1]]) & 4095);
851 ether_type = u16::from_be_bytes([bytes[at + 2], bytes[at + 3]]);
852 at += 4;
853 }
854 link = Some(LinkLayer {
855 source_mac: mac_text(&bytes[6..12]),
856 destination_mac: mac_text(&bytes[..6]),
857 ether_type: u32::from(ether_type),
858 vlans,
859 });
860 match ether_type {
861 0x800 => 11,
862 0x86dd => 12,
863 _ => return (None, link, "unsupported_network_protocol"),
864 }
865 } else {
866 protocol
867 };
868 let b = &bytes[at..];
869 let (mut p, mut offset, non_initial) = match ip_protocol {
870 11 => {
871 if b.len() < 20 {
872 return (None, link, "truncated");
873 }
874 let n = usize::from(b[0] & 15) * 4;
875 if b[0] >> 4 != 4 || n < 20 {
876 return (None, link, "invalid_ip_header");
877 }
878 if b.len() < n {
879 return (None, link, "truncated");
880 }
881 (
882 Packet {
883 source_ip: IpAddr::V4(Ipv4Addr::new(b[12], b[13], b[14], b[15])),
884 destination_ip: IpAddr::V4(Ipv4Addr::new(b[16], b[17], b[18], b[19])),
885 packet_length: u32::from(u16::from_be_bytes([b[2], b[3]])),
886 protocol: b[9],
887 source_port: 0,
888 destination_port: 0,
889 tcp_flags: 0,
890 traffic_class: b[1],
891 },
892 n,
893 u16::from_be_bytes([b[6], b[7]]) & 0x1fff != 0,
894 )
895 }
896 12 => {
897 if b.len() < 40 {
898 return (None, link, "truncated");
899 }
900 if b[0] >> 4 != 6 {
901 return (None, link, "invalid_ip_header");
902 }
903 (
904 Packet {
905 source_ip: IpAddr::V6(Ipv6Addr::from(<[u8; 16]>::try_from(&b[8..24]).unwrap())),
906 destination_ip: IpAddr::V6(Ipv6Addr::from(
907 <[u8; 16]>::try_from(&b[24..40]).unwrap(),
908 )),
909 packet_length: 40 + u32::from(u16::from_be_bytes([b[4], b[5]])),
910 protocol: b[6],
911 source_port: 0,
912 destination_port: 0,
913 tcp_flags: 0,
914 traffic_class: ((b[0] & 15) << 4) | (b[1] >> 4),
915 },
916 40,
917 false,
918 )
919 }
920 _ => return (None, link, "unsupported_header_protocol"),
921 };
922 if non_initial {
923 return (Some(p), link, "non_initial_fragment");
924 }
925 let mut extensions = 0;
926 if ip_protocol == 12 {
927 while [0, 43, 44, 51, 60].contains(&p.protocol) {
928 if extensions == MAX_IPV6_EXTENSIONS {
929 return (Some(p), link, "extension_depth_limit");
930 }
931 extensions += 1;
932 if b.len() < offset + 2 {
933 return (Some(p), link, "truncated");
934 }
935 let n = match p.protocol {
936 44 => 8,
937 51 => (usize::from(b[offset + 1]) + 2) * 4,
938 _ => (usize::from(b[offset + 1]) + 1) * 8,
939 };
940 if b.len() < offset + n {
941 return (Some(p), link, "truncated");
942 }
943 let fragmented = p.protocol == 44
944 && u16::from_be_bytes([b[offset + 2], b[offset + 3]]) & 0xfff8 != 0;
945 p.protocol = b[offset];
946 offset += n;
947 if fragmented {
948 return (Some(p), link, "non_initial_fragment");
949 }
950 }
951 }
952 if p.protocol == 6 || p.protocol == 17 {
953 if b.len() < offset + 4 {
954 return (Some(p), link, "truncated");
955 }
956 p.source_port = u16::from_be_bytes([b[offset], b[offset + 1]]);
957 p.destination_port = u16::from_be_bytes([b[offset + 2], b[offset + 3]]);
958 if p.protocol == 6 {
959 if b.len() < offset + 14 {
960 return (Some(p), link, "truncated");
961 }
962 p.tcp_flags = (u32::from(b[offset + 12] & 1) << 8) | u32::from(b[offset + 13]);
963 }
964 }
965 (Some(p), link, "decoded")
966}
967struct Session {
968 expected: u32,
969 uptime: u32,
970 last_seen: Instant,
971}
972pub struct SequenceCache {
973 sessions: BTreeMap<(SocketAddr, IpAddr, u32), Session>,
974 idle: Duration,
975}
976impl SequenceCache {
977 pub fn new(idle: Duration) -> Self {
978 Self {
979 sessions: BTreeMap::new(),
980 idle,
981 }
982 }
983 pub fn count(&self) -> usize {
984 self.sessions.len()
985 }
986 pub fn expire(&mut self, now: Instant) {
987 self.sessions
988 .retain(|_, s| now.duration_since(s.last_seen) < self.idle);
989 }
990 pub fn ingest(&mut self, peer: SocketAddr, bytes: &[u8], now: Instant) -> Result<Message> {
991 self.expire(now);
992 let mut m = decode(bytes)?;
993 let key = (peer, m.agent_address, m.sub_agent_id);
994 ensure!(
995 self.sessions.contains_key(&key) || self.sessions.len() < MAX_SESSIONS,
996 "sFlow session cap128"
997 );
998 let mut advance = true;
999 if let Some(s) = self.sessions.get(&key) {
1000 let delta = m.sequence_number.wrapping_sub(s.expected);
1001 m.sequence_tracking = SequenceTracking {
1002 expected: Some(s.expected),
1003 status: if delta == 0 {
1004 "in_order"
1005 } else if delta < 0x80000000 {
1006 "gap"
1007 } else {
1008 advance = false;
1009 "out_of_order_or_duplicate"
1010 },
1011 missing_datagrams: if delta > 0 && delta < 0x80000000 {
1012 Some(delta)
1013 } else {
1014 None
1015 },
1016 uptime_decreased: m.uptime_ms.wrapping_sub(s.uptime) >= 0x80000000,
1019 };
1020 }
1021 if advance {
1022 self.sessions.insert(
1023 key,
1024 Session {
1025 expected: m.sequence_number.wrapping_add(1),
1026 uptime: m.uptime_ms,
1027 last_seen: now,
1028 },
1029 );
1030 } else if let Some(s) = self.sessions.get_mut(&key) {
1031 s.last_seen = now;
1032 }
1033 Ok(m)
1034 }
1035}