1use std::collections::HashSet;
4
5use smallvec::SmallVec;
6
7use etherparse::{TcpHeaderSlice, TcpOptionElement};
8
9use super::{FieldValue, ParseContext, ParseResult, PayloadMode, Protocol};
10use crate::schema::{DataKind, FieldDescriptor};
11
12pub const IP_PROTO_TCP: u8 = 6;
14
15pub mod flags {
17 pub const FIN: u16 = 0x001;
18 pub const SYN: u16 = 0x002;
19 pub const RST: u16 = 0x004;
20 pub const PSH: u16 = 0x008;
21 pub const ACK: u16 = 0x010;
22 pub const URG: u16 = 0x020;
23 pub const ECE: u16 = 0x040;
24 pub const CWR: u16 = 0x080;
25 pub const NS: u16 = 0x100;
26}
27
28#[allow(dead_code)]
30pub mod options {
31 pub const END_OF_LIST: u8 = 0;
32 pub const NOP: u8 = 1;
33 pub const MSS: u8 = 2;
34 pub const WINDOW_SCALE: u8 = 3;
35 pub const SACK_PERMITTED: u8 = 4;
36 pub const SACK: u8 = 5;
37 pub const TIMESTAMP: u8 = 8;
38}
39
40#[derive(Debug, Clone, Default)]
43pub struct ParsedTcpOptions {
44 pub option_names: SmallVec<[&'static str; 4]>,
46 pub mss: Option<u16>,
48 pub window_scale: Option<u8>,
50 pub sack_permitted: bool,
52 pub sack_left_edges: SmallVec<[u32; 4]>,
54 pub sack_right_edges: SmallVec<[u32; 4]>,
56 pub ts_val: Option<u32>,
58 pub ts_ecr: Option<u32>,
60}
61
62fn get_options_static(opts: &ParsedTcpOptions) -> Option<&'static str> {
65 let mut mask: u8 = 0;
67 for name in &opts.option_names {
68 match *name {
69 "MSS" => mask |= 1,
70 "WS" => mask |= 2,
71 "SACK_PERM" => mask |= 4,
72 "SACK" => mask |= 8,
73 "TS" => mask |= 16,
74 _ => return None, }
76 }
77
78 match mask {
80 0b00001 => Some("MSS"),
81 0b00010 => Some("WS"),
82 0b00011 => Some("MSS,WS"),
83 0b00100 => Some("SACK_PERM"),
84 0b00101 => Some("MSS,SACK_PERM"),
85 0b00111 => Some("MSS,WS,SACK_PERM"),
86 0b01000 => Some("SACK"),
87 0b10000 => Some("TS"),
88 0b10001 => Some("MSS,TS"),
89 0b10010 => Some("WS,TS"),
90 0b10011 => Some("MSS,WS,TS"),
91 0b10100 => Some("SACK_PERM,TS"),
92 0b10101 => Some("MSS,SACK_PERM,TS"),
93 0b10111 => Some("MSS,WS,SACK_PERM,TS"),
94 0b11000 => Some("SACK,TS"),
95 0b11111 => Some("MSS,WS,SACK_PERM,SACK,TS"),
96 _ => None, }
98}
99
100fn parse_tcp_options(tcp: &TcpHeaderSlice<'_>) -> ParsedTcpOptions {
103 let mut result = ParsedTcpOptions::default();
104
105 for opt_result in tcp.options_iterator() {
106 let opt = match opt_result {
107 Ok(o) => o,
108 Err(_) => continue, };
110
111 match opt {
112 TcpOptionElement::Noop => {
113 }
115 TcpOptionElement::MaximumSegmentSize(mss) => {
116 result.mss = Some(mss);
117 result.option_names.push("MSS");
118 }
119 TcpOptionElement::WindowScale(scale) => {
120 result.window_scale = Some(scale);
121 result.option_names.push("WS");
122 }
123 TcpOptionElement::SelectiveAcknowledgementPermitted => {
124 result.sack_permitted = true;
125 result.option_names.push("SACK_PERM");
126 }
127 TcpOptionElement::SelectiveAcknowledgement(first, rest) => {
128 result.sack_left_edges.push(first.0);
130 result.sack_right_edges.push(first.1);
131 for block in rest.into_iter().flatten() {
132 result.sack_left_edges.push(block.0);
133 result.sack_right_edges.push(block.1);
134 }
135 result.option_names.push("SACK");
136 }
137 TcpOptionElement::Timestamp(ts_val, ts_ecr) => {
138 result.ts_val = Some(ts_val);
139 result.ts_ecr = Some(ts_ecr);
140 result.option_names.push("TS");
141 }
142 }
143 }
144
145 result
146}
147
148#[derive(Debug, Clone, Copy)]
150pub struct TcpProtocol;
151
152impl Protocol for TcpProtocol {
153 fn name(&self) -> &'static str {
154 "tcp"
155 }
156
157 fn display_name(&self) -> &'static str {
158 "TCP"
159 }
160
161 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
162 match context.hint("ip_protocol") {
163 Some(proto) if proto == IP_PROTO_TCP as u64 => Some(100),
164 _ => None,
165 }
166 }
167
168 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
169 match TcpHeaderSlice::from_slice(data) {
170 Ok(tcp) => {
171 let mut fields = SmallVec::new();
172
173 fields.push(("src_port", FieldValue::UInt16(tcp.source_port())));
174 fields.push(("dst_port", FieldValue::UInt16(tcp.destination_port())));
175 fields.push(("seq", FieldValue::UInt32(tcp.sequence_number())));
176 fields.push(("ack", FieldValue::UInt32(tcp.acknowledgment_number())));
177 fields.push(("data_offset", FieldValue::UInt8(tcp.data_offset())));
178
179 let mut tcp_flags: u16 = 0;
181 if tcp.fin() {
182 tcp_flags |= flags::FIN;
183 }
184 if tcp.syn() {
185 tcp_flags |= flags::SYN;
186 }
187 if tcp.rst() {
188 tcp_flags |= flags::RST;
189 }
190 if tcp.psh() {
191 tcp_flags |= flags::PSH;
192 }
193 if tcp.ack() {
194 tcp_flags |= flags::ACK;
195 }
196 if tcp.urg() {
197 tcp_flags |= flags::URG;
198 }
199 if tcp.ece() {
200 tcp_flags |= flags::ECE;
201 }
202 if tcp.cwr() {
203 tcp_flags |= flags::CWR;
204 }
205 if tcp.ns() {
206 tcp_flags |= flags::NS;
207 }
208 fields.push(("flags", FieldValue::UInt16(tcp_flags)));
209
210 fields.push(("flag_fin", FieldValue::Bool(tcp.fin())));
212 fields.push(("flag_syn", FieldValue::Bool(tcp.syn())));
213 fields.push(("flag_rst", FieldValue::Bool(tcp.rst())));
214 fields.push(("flag_psh", FieldValue::Bool(tcp.psh())));
215 fields.push(("flag_ack", FieldValue::Bool(tcp.ack())));
216 fields.push(("flag_urg", FieldValue::Bool(tcp.urg())));
217
218 fields.push(("window", FieldValue::UInt16(tcp.window_size())));
219 fields.push(("checksum", FieldValue::UInt16(tcp.checksum())));
220 fields.push(("urgent_ptr", FieldValue::UInt16(tcp.urgent_pointer())));
221
222 let header_len = tcp.slice().len();
224 let options_len = header_len.saturating_sub(20);
225 fields.push(("options_length", FieldValue::UInt8(options_len as u8)));
226
227 if options_len > 0 {
229 let opts = parse_tcp_options(&tcp);
230
231 if !opts.option_names.is_empty() {
233 let options_str = if let Some(static_str) = get_options_static(&opts) {
234 FieldValue::Str(static_str) } else {
236 FieldValue::OwnedString(opts.option_names.join(",").into())
237 };
238 fields.push(("options", options_str));
239 } else {
240 fields.push(("options", FieldValue::Null));
241 }
242
243 if let Some(mss) = opts.mss {
245 fields.push(("mss", FieldValue::UInt16(mss)));
246 } else {
247 fields.push(("mss", FieldValue::Null));
248 }
249
250 if let Some(ws) = opts.window_scale {
252 fields.push(("window_scale", FieldValue::UInt8(ws)));
253 } else {
254 fields.push(("window_scale", FieldValue::Null));
255 }
256
257 if opts.sack_permitted {
259 fields.push(("sack_permitted", FieldValue::Bool(true)));
260 } else {
261 fields.push(("sack_permitted", FieldValue::Null));
262 }
263
264 if !opts.sack_left_edges.is_empty() {
266 let left_edges: Vec<FieldValue> = opts
267 .sack_left_edges
268 .iter()
269 .map(|&v| FieldValue::UInt32(v))
270 .collect();
271 let right_edges: Vec<FieldValue> = opts
272 .sack_right_edges
273 .iter()
274 .map(|&v| FieldValue::UInt32(v))
275 .collect();
276 fields.push(("sack_left_edges", FieldValue::List(left_edges)));
277 fields.push(("sack_right_edges", FieldValue::List(right_edges)));
278 } else {
279 fields.push(("sack_left_edges", FieldValue::Null));
280 fields.push(("sack_right_edges", FieldValue::Null));
281 }
282
283 if let Some(ts_val) = opts.ts_val {
285 fields.push(("ts_val", FieldValue::UInt32(ts_val)));
286 } else {
287 fields.push(("ts_val", FieldValue::Null));
288 }
289 if let Some(ts_ecr) = opts.ts_ecr {
290 fields.push(("ts_ecr", FieldValue::UInt32(ts_ecr)));
291 } else {
292 fields.push(("ts_ecr", FieldValue::Null));
293 }
294 } else {
295 fields.push(("options", FieldValue::Null));
297 fields.push(("mss", FieldValue::Null));
298 fields.push(("window_scale", FieldValue::Null));
299 fields.push(("sack_permitted", FieldValue::Null));
300 fields.push(("sack_left_edges", FieldValue::Null));
301 fields.push(("sack_right_edges", FieldValue::Null));
302 fields.push(("ts_val", FieldValue::Null));
303 fields.push(("ts_ecr", FieldValue::Null));
304 }
305
306 let mut child_hints = SmallVec::new();
307 child_hints.push(("src_port", tcp.source_port() as u64));
308 child_hints.push(("dst_port", tcp.destination_port() as u64));
309 child_hints.push(("transport", 6)); ParseResult::success(fields, &data[header_len..], child_hints)
312 }
313 Err(e) => ParseResult::error(format!("TCP parse error: {e}"), data),
314 }
315 }
316
317 fn schema_fields(&self) -> Vec<FieldDescriptor> {
318 vec![
319 FieldDescriptor::new("tcp.src_port", DataKind::UInt16).set_nullable(true),
320 FieldDescriptor::new("tcp.dst_port", DataKind::UInt16).set_nullable(true),
321 FieldDescriptor::new("tcp.seq", DataKind::UInt32).set_nullable(true),
322 FieldDescriptor::new("tcp.ack", DataKind::UInt32).set_nullable(true),
323 FieldDescriptor::new("tcp.data_offset", DataKind::UInt8).set_nullable(true),
324 FieldDescriptor::new("tcp.flags", DataKind::UInt16).set_nullable(true),
325 FieldDescriptor::new("tcp.flag_fin", DataKind::Bool).set_nullable(true),
326 FieldDescriptor::new("tcp.flag_syn", DataKind::Bool).set_nullable(true),
327 FieldDescriptor::new("tcp.flag_rst", DataKind::Bool).set_nullable(true),
328 FieldDescriptor::new("tcp.flag_psh", DataKind::Bool).set_nullable(true),
329 FieldDescriptor::new("tcp.flag_ack", DataKind::Bool).set_nullable(true),
330 FieldDescriptor::new("tcp.flag_urg", DataKind::Bool).set_nullable(true),
331 FieldDescriptor::new("tcp.window", DataKind::UInt16).set_nullable(true),
332 FieldDescriptor::new("tcp.checksum", DataKind::UInt16).set_nullable(true),
333 FieldDescriptor::new("tcp.urgent_ptr", DataKind::UInt16).set_nullable(true),
334 FieldDescriptor::new("tcp.options_length", DataKind::UInt8).set_nullable(true),
335 FieldDescriptor::new("tcp.options", DataKind::String)
337 .set_nullable(true)
338 .with_description("Comma-separated list of TCP options present (e.g., MSS,WS,TS)"),
339 FieldDescriptor::new("tcp.mss", DataKind::UInt16)
340 .set_nullable(true)
341 .with_description("Maximum Segment Size (option 2)"),
342 FieldDescriptor::new("tcp.window_scale", DataKind::UInt8)
343 .set_nullable(true)
344 .with_description("Window scale factor (option 3)"),
345 FieldDescriptor::new("tcp.sack_permitted", DataKind::Bool)
346 .set_nullable(true)
347 .with_description("Selective ACK permitted (option 4)"),
348 FieldDescriptor::new(
349 "tcp.sack_left_edges",
350 DataKind::List(Box::new(DataKind::UInt32)),
351 )
352 .set_nullable(true)
353 .with_description("SACK block left edges (option 5)"),
354 FieldDescriptor::new(
355 "tcp.sack_right_edges",
356 DataKind::List(Box::new(DataKind::UInt32)),
357 )
358 .set_nullable(true)
359 .with_description("SACK block right edges (option 5)"),
360 FieldDescriptor::new("tcp.ts_val", DataKind::UInt32)
361 .set_nullable(true)
362 .with_description("TCP timestamp value (TSval) - sender's timestamp (option 8)"),
363 FieldDescriptor::new("tcp.ts_ecr", DataKind::UInt32)
364 .set_nullable(true)
365 .with_description("TCP timestamp echo reply (TSecr) - echoed timestamp (option 8)"),
366 ]
367 }
368
369 fn child_protocols(&self) -> &[&'static str] {
370 &[] }
372
373 fn payload_mode(&self) -> PayloadMode {
374 PayloadMode::Stream
375 }
376
377 fn dependencies(&self) -> &'static [&'static str] {
378 &["ipv4", "ipv6"]
379 }
380
381 fn parse_projected<'a>(
382 &self,
383 data: &'a [u8],
384 _context: &ParseContext,
385 fields: Option<&HashSet<String>>,
386 ) -> ParseResult<'a> {
387 let fields = match fields {
389 None => return self.parse(data, _context),
390 Some(f) if f.is_empty() => return self.parse(data, _context),
391 Some(f) => f,
392 };
393
394 match TcpHeaderSlice::from_slice(data) {
395 Ok(tcp) => {
396 let mut result_fields = SmallVec::new();
397 let header_len = tcp.slice().len();
398
399 let src_port = tcp.source_port();
401 let dst_port = tcp.destination_port();
402
403 if fields.contains("src_port") {
405 result_fields.push(("src_port", FieldValue::UInt16(src_port)));
406 }
407 if fields.contains("dst_port") {
408 result_fields.push(("dst_port", FieldValue::UInt16(dst_port)));
409 }
410 if fields.contains("seq") {
411 result_fields.push(("seq", FieldValue::UInt32(tcp.sequence_number())));
412 }
413 if fields.contains("ack") {
414 result_fields.push(("ack", FieldValue::UInt32(tcp.acknowledgment_number())));
415 }
416 if fields.contains("data_offset") {
417 result_fields.push(("data_offset", FieldValue::UInt8(tcp.data_offset())));
418 }
419
420 let need_flags = fields.contains("flags")
422 || fields.contains("flag_fin")
423 || fields.contains("flag_syn")
424 || fields.contains("flag_rst")
425 || fields.contains("flag_psh")
426 || fields.contains("flag_ack")
427 || fields.contains("flag_urg");
428
429 if need_flags {
430 let mut tcp_flags: u16 = 0;
431 if tcp.fin() {
432 tcp_flags |= flags::FIN;
433 }
434 if tcp.syn() {
435 tcp_flags |= flags::SYN;
436 }
437 if tcp.rst() {
438 tcp_flags |= flags::RST;
439 }
440 if tcp.psh() {
441 tcp_flags |= flags::PSH;
442 }
443 if tcp.ack() {
444 tcp_flags |= flags::ACK;
445 }
446 if tcp.urg() {
447 tcp_flags |= flags::URG;
448 }
449 if tcp.ece() {
450 tcp_flags |= flags::ECE;
451 }
452 if tcp.cwr() {
453 tcp_flags |= flags::CWR;
454 }
455 if tcp.ns() {
456 tcp_flags |= flags::NS;
457 }
458
459 if fields.contains("flags") {
460 result_fields.push(("flags", FieldValue::UInt16(tcp_flags)));
461 }
462 if fields.contains("flag_fin") {
463 result_fields.push(("flag_fin", FieldValue::Bool(tcp.fin())));
464 }
465 if fields.contains("flag_syn") {
466 result_fields.push(("flag_syn", FieldValue::Bool(tcp.syn())));
467 }
468 if fields.contains("flag_rst") {
469 result_fields.push(("flag_rst", FieldValue::Bool(tcp.rst())));
470 }
471 if fields.contains("flag_psh") {
472 result_fields.push(("flag_psh", FieldValue::Bool(tcp.psh())));
473 }
474 if fields.contains("flag_ack") {
475 result_fields.push(("flag_ack", FieldValue::Bool(tcp.ack())));
476 }
477 if fields.contains("flag_urg") {
478 result_fields.push(("flag_urg", FieldValue::Bool(tcp.urg())));
479 }
480 }
481
482 if fields.contains("window") {
483 result_fields.push(("window", FieldValue::UInt16(tcp.window_size())));
484 }
485 if fields.contains("checksum") {
486 result_fields.push(("checksum", FieldValue::UInt16(tcp.checksum())));
487 }
488 if fields.contains("urgent_ptr") {
489 result_fields.push(("urgent_ptr", FieldValue::UInt16(tcp.urgent_pointer())));
490 }
491 if fields.contains("options_length") {
492 let options_len = header_len.saturating_sub(20);
493 result_fields.push(("options_length", FieldValue::UInt8(options_len as u8)));
494 }
495
496 let need_options = fields.contains("options")
498 || fields.contains("mss")
499 || fields.contains("window_scale")
500 || fields.contains("sack_permitted")
501 || fields.contains("sack_left_edges")
502 || fields.contains("sack_right_edges")
503 || fields.contains("ts_val")
504 || fields.contains("ts_ecr");
505
506 if need_options {
507 let options_len = header_len.saturating_sub(20);
508 if options_len > 0 {
509 let opts = parse_tcp_options(&tcp);
510
511 if fields.contains("options") {
512 if !opts.option_names.is_empty() {
513 let options_str =
514 if let Some(static_str) = get_options_static(&opts) {
515 FieldValue::Str(static_str)
516 } else {
517 FieldValue::OwnedString(opts.option_names.join(",").into())
518 };
519 result_fields.push(("options", options_str));
520 } else {
521 result_fields.push(("options", FieldValue::Null));
522 }
523 }
524 if fields.contains("mss") {
525 if let Some(mss) = opts.mss {
526 result_fields.push(("mss", FieldValue::UInt16(mss)));
527 } else {
528 result_fields.push(("mss", FieldValue::Null));
529 }
530 }
531 if fields.contains("window_scale") {
532 if let Some(ws) = opts.window_scale {
533 result_fields.push(("window_scale", FieldValue::UInt8(ws)));
534 } else {
535 result_fields.push(("window_scale", FieldValue::Null));
536 }
537 }
538 if fields.contains("sack_permitted") {
539 if opts.sack_permitted {
540 result_fields.push(("sack_permitted", FieldValue::Bool(true)));
541 } else {
542 result_fields.push(("sack_permitted", FieldValue::Null));
543 }
544 }
545 let need_sack_edges = fields.contains("sack_left_edges")
546 || fields.contains("sack_right_edges");
547 if need_sack_edges {
548 if !opts.sack_left_edges.is_empty() {
549 if fields.contains("sack_left_edges") {
550 let left_edges: Vec<FieldValue> = opts
551 .sack_left_edges
552 .iter()
553 .map(|&v| FieldValue::UInt32(v))
554 .collect();
555 result_fields
556 .push(("sack_left_edges", FieldValue::List(left_edges)));
557 }
558 if fields.contains("sack_right_edges") {
559 let right_edges: Vec<FieldValue> = opts
560 .sack_right_edges
561 .iter()
562 .map(|&v| FieldValue::UInt32(v))
563 .collect();
564 result_fields
565 .push(("sack_right_edges", FieldValue::List(right_edges)));
566 }
567 } else {
568 if fields.contains("sack_left_edges") {
569 result_fields.push(("sack_left_edges", FieldValue::Null));
570 }
571 if fields.contains("sack_right_edges") {
572 result_fields.push(("sack_right_edges", FieldValue::Null));
573 }
574 }
575 }
576 if fields.contains("ts_val") {
577 if let Some(ts_val) = opts.ts_val {
578 result_fields.push(("ts_val", FieldValue::UInt32(ts_val)));
579 } else {
580 result_fields.push(("ts_val", FieldValue::Null));
581 }
582 }
583 if fields.contains("ts_ecr") {
584 if let Some(ts_ecr) = opts.ts_ecr {
585 result_fields.push(("ts_ecr", FieldValue::UInt32(ts_ecr)));
586 } else {
587 result_fields.push(("ts_ecr", FieldValue::Null));
588 }
589 }
590 } else {
591 if fields.contains("options") {
593 result_fields.push(("options", FieldValue::Null));
594 }
595 if fields.contains("mss") {
596 result_fields.push(("mss", FieldValue::Null));
597 }
598 if fields.contains("window_scale") {
599 result_fields.push(("window_scale", FieldValue::Null));
600 }
601 if fields.contains("sack_permitted") {
602 result_fields.push(("sack_permitted", FieldValue::Null));
603 }
604 if fields.contains("sack_left_edges") {
605 result_fields.push(("sack_left_edges", FieldValue::Null));
606 }
607 if fields.contains("sack_right_edges") {
608 result_fields.push(("sack_right_edges", FieldValue::Null));
609 }
610 if fields.contains("ts_val") {
611 result_fields.push(("ts_val", FieldValue::Null));
612 }
613 if fields.contains("ts_ecr") {
614 result_fields.push(("ts_ecr", FieldValue::Null));
615 }
616 }
617 }
618
619 let mut child_hints = SmallVec::new();
621 child_hints.push(("src_port", src_port as u64));
622 child_hints.push(("dst_port", dst_port as u64));
623 child_hints.push(("transport", 6)); ParseResult::success(result_fields, &data[header_len..], child_hints)
626 }
627 Err(e) => ParseResult::error(format!("TCP parse error: {e}"), data),
628 }
629 }
630
631 fn cheap_fields(&self) -> &'static [&'static str] {
632 &[
634 "src_port",
635 "dst_port",
636 "seq",
637 "ack",
638 "data_offset",
639 "flags",
640 "flag_fin",
641 "flag_syn",
642 "flag_rst",
643 "flag_psh",
644 "flag_ack",
645 "flag_urg",
646 "window",
647 "checksum",
648 "urgent_ptr",
649 ]
650 }
651
652 fn expensive_fields(&self) -> &'static [&'static str] {
653 &[
655 "options_length",
656 "options",
657 "mss",
658 "window_scale",
659 "sack_permitted",
660 "sack_left_edges",
661 "sack_right_edges",
662 "ts_val",
663 "ts_ecr",
664 ]
665 }
666}
667
668#[cfg(test)]
669mod tests {
670 use super::*;
671
672 #[test]
673 fn test_options_static_lookup() {
674 let mut opts = ParsedTcpOptions::default();
676 opts.option_names.push("MSS");
677 opts.option_names.push("WS");
678 opts.option_names.push("SACK_PERM");
679 opts.option_names.push("TS");
680
681 let result = get_options_static(&opts);
682 assert_eq!(result, Some("MSS,WS,SACK_PERM,TS"));
683
684 let mut opts_mss = ParsedTcpOptions::default();
686 opts_mss.option_names.push("MSS");
687 assert_eq!(get_options_static(&opts_mss), Some("MSS"));
688
689 let mut opts_unknown = ParsedTcpOptions::default();
691 opts_unknown.option_names.push("UNKNOWN");
692 assert_eq!(get_options_static(&opts_unknown), None);
693 }
694
695 #[test]
696 fn test_parse_tcp_syn() {
697 let header = [
699 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, ];
709
710 let parser = TcpProtocol;
711 let mut context = ParseContext::new(1);
712 context.insert_hint("ip_protocol", 6);
713
714 let result = parser.parse(&header, &context);
715
716 assert!(result.is_ok());
717 assert_eq!(result.get("src_port"), Some(&FieldValue::UInt16(80)));
718 assert_eq!(result.get("dst_port"), Some(&FieldValue::UInt16(8080)));
719 assert_eq!(result.get("flag_syn"), Some(&FieldValue::Bool(true)));
720 assert_eq!(result.get("flag_ack"), Some(&FieldValue::Bool(false)));
721 assert_eq!(result.get("flags"), Some(&FieldValue::UInt16(flags::SYN)));
722 }
723
724 #[test]
725 fn test_parse_tcp_syn_ack() {
726 let header = [
727 0x1f, 0x90, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x12, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, ];
737
738 let parser = TcpProtocol;
739 let mut context = ParseContext::new(1);
740 context.insert_hint("ip_protocol", 6);
741
742 let result = parser.parse(&header, &context);
743
744 assert!(result.is_ok());
745 assert_eq!(result.get("flag_syn"), Some(&FieldValue::Bool(true)));
746 assert_eq!(result.get("flag_ack"), Some(&FieldValue::Bool(true)));
747 assert_eq!(
748 result.get("flags"),
749 Some(&FieldValue::UInt16(flags::SYN | flags::ACK))
750 );
751 assert_eq!(result.get("seq"), Some(&FieldValue::UInt32(4096)));
752 assert_eq!(result.get("ack"), Some(&FieldValue::UInt32(2)));
753 }
754
755 #[test]
756 fn test_parse_tcp_fin_ack() {
757 let header = [
758 0x00, 0x50, 0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x50, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ];
768
769 let parser = TcpProtocol;
770 let mut context = ParseContext::new(1);
771 context.insert_hint("ip_protocol", 6);
772
773 let result = parser.parse(&header, &context);
774
775 assert!(result.is_ok());
776 assert_eq!(result.get("flag_fin"), Some(&FieldValue::Bool(true)));
777 assert_eq!(result.get("flag_ack"), Some(&FieldValue::Bool(true)));
778 assert_eq!(result.get("flag_syn"), Some(&FieldValue::Bool(false)));
779 }
780
781 #[test]
782 fn test_parse_tcp_rst() {
783 let header = [
784 0x00, 0x50, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
794
795 let parser = TcpProtocol;
796 let mut context = ParseContext::new(1);
797 context.insert_hint("ip_protocol", 6);
798
799 let result = parser.parse(&header, &context);
800
801 assert!(result.is_ok());
802 assert_eq!(result.get("flag_rst"), Some(&FieldValue::Bool(true)));
803 assert_eq!(result.get("flags"), Some(&FieldValue::UInt16(flags::RST)));
804 }
805
806 #[test]
807 fn test_parse_tcp_psh_ack() {
808 let header = [
809 0x01, 0xbb, 0xd4, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x50, 0x18, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x54, 0x54, 0x50, ];
821
822 let parser = TcpProtocol;
823 let mut context = ParseContext::new(1);
824 context.insert_hint("ip_protocol", 6);
825
826 let result = parser.parse(&header, &context);
827
828 assert!(result.is_ok());
829 assert_eq!(result.get("src_port"), Some(&FieldValue::UInt16(443)));
830 assert_eq!(result.get("dst_port"), Some(&FieldValue::UInt16(54321)));
831 assert_eq!(result.get("flag_psh"), Some(&FieldValue::Bool(true)));
832 assert_eq!(result.get("flag_ack"), Some(&FieldValue::Bool(true)));
833 assert_eq!(result.remaining.len(), 4); }
835
836 #[test]
837 fn test_can_parse_tcp() {
838 let parser = TcpProtocol;
839
840 let ctx1 = ParseContext::new(1);
842 assert!(parser.can_parse(&ctx1).is_none());
843
844 let mut ctx2 = ParseContext::new(1);
846 ctx2.insert_hint("ip_protocol", 17); assert!(parser.can_parse(&ctx2).is_none());
848
849 let mut ctx3 = ParseContext::new(1);
851 ctx3.insert_hint("ip_protocol", 6);
852 assert!(parser.can_parse(&ctx3).is_some());
853 }
854
855 #[test]
856 fn test_parse_tcp_too_short() {
857 let short_header = [0x00, 0x50, 0x1f, 0x90]; let parser = TcpProtocol;
860 let mut context = ParseContext::new(1);
861 context.insert_hint("ip_protocol", 6);
862
863 let result = parser.parse(&short_header, &context);
864
865 assert!(!result.is_ok());
866 assert!(result.error.is_some());
867 }
868
869 #[test]
870 fn test_tcp_child_hints() {
871 let header = [
872 0x01, 0xbb, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, ];
882
883 let parser = TcpProtocol;
884 let mut context = ParseContext::new(1);
885 context.insert_hint("ip_protocol", 6);
886
887 let result = parser.parse(&header, &context);
888
889 assert!(result.is_ok());
890 assert_eq!(result.hint("src_port"), Some(443u64));
891 assert_eq!(result.hint("dst_port"), Some(80u64));
892 assert_eq!(result.hint("transport"), Some(6u64));
893 }
894
895 #[test]
896 fn test_tcp_projected_parsing_ports_only() {
897 let header = [
898 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, ];
908
909 let parser = TcpProtocol;
910 let mut context = ParseContext::new(1);
911 context.insert_hint("ip_protocol", 6);
912
913 let fields: HashSet<String> = ["src_port", "dst_port"]
915 .iter()
916 .map(|s| s.to_string())
917 .collect();
918 let result = parser.parse_projected(&header, &context, Some(&fields));
919
920 assert!(result.is_ok());
921 assert_eq!(result.get("src_port"), Some(&FieldValue::UInt16(80)));
923 assert_eq!(result.get("dst_port"), Some(&FieldValue::UInt16(8080)));
924 assert!(result.get("seq").is_none());
926 assert!(result.get("ack").is_none());
927 assert!(result.get("flags").is_none());
928 assert!(result.get("flag_syn").is_none());
929 assert_eq!(result.hint("src_port"), Some(80u64));
931 assert_eq!(result.hint("dst_port"), Some(8080u64));
932 }
933
934 #[test]
935 fn test_tcp_projected_parsing_with_flags() {
936 let header = [
937 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x12, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, ];
947
948 let parser = TcpProtocol;
949 let mut context = ParseContext::new(1);
950 context.insert_hint("ip_protocol", 6);
951
952 let fields: HashSet<String> = ["src_port", "flag_syn", "flag_ack"]
954 .iter()
955 .map(|s| s.to_string())
956 .collect();
957 let result = parser.parse_projected(&header, &context, Some(&fields));
958
959 assert!(result.is_ok());
960 assert_eq!(result.get("src_port"), Some(&FieldValue::UInt16(80)));
961 assert_eq!(result.get("flag_syn"), Some(&FieldValue::Bool(true)));
962 assert_eq!(result.get("flag_ack"), Some(&FieldValue::Bool(true)));
963 assert!(result.get("flags").is_none());
965 assert!(result.get("dst_port").is_none());
967 assert!(result.get("seq").is_none());
968 }
969
970 #[test]
971 fn test_tcp_projected_parsing_none_uses_full() {
972 let header = [
973 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, ];
983
984 let parser = TcpProtocol;
985 let mut context = ParseContext::new(1);
986 context.insert_hint("ip_protocol", 6);
987
988 let result = parser.parse_projected(&header, &context, None);
990
991 assert!(result.is_ok());
992 assert!(result.get("src_port").is_some());
994 assert!(result.get("dst_port").is_some());
995 assert!(result.get("seq").is_some());
996 assert!(result.get("ack").is_some());
997 assert!(result.get("flags").is_some());
998 assert!(result.get("flag_syn").is_some());
999 assert!(result.get("window").is_some());
1000 }
1001
1002 #[test]
1003 fn test_parse_tcp_timestamp_option() {
1004 let header = [
1007 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x80, 0x10, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, ];
1024
1025 let parser = TcpProtocol;
1026 let mut context = ParseContext::new(1);
1027 context.insert_hint("ip_protocol", 6);
1028
1029 let result = parser.parse(&header, &context);
1030
1031 assert!(result.is_ok());
1032 assert_eq!(result.get("options_length"), Some(&FieldValue::UInt8(12)));
1033 assert_eq!(result.get("ts_val"), Some(&FieldValue::UInt32(0x12345678)));
1034 assert_eq!(result.get("ts_ecr"), Some(&FieldValue::UInt32(0x9abcdef0)));
1035 }
1036
1037 #[test]
1038 fn test_parse_tcp_no_timestamp() {
1039 let header = [
1041 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, ];
1051
1052 let parser = TcpProtocol;
1053 let mut context = ParseContext::new(1);
1054 context.insert_hint("ip_protocol", 6);
1055
1056 let result = parser.parse(&header, &context);
1057
1058 assert!(result.is_ok());
1059 assert_eq!(result.get("options_length"), Some(&FieldValue::UInt8(0)));
1060 assert_eq!(result.get("ts_val"), Some(&FieldValue::Null));
1061 assert_eq!(result.get("ts_ecr"), Some(&FieldValue::Null));
1062 }
1063
1064 #[test]
1065 fn test_parse_tcp_mss_option() {
1066 let header = [
1069 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, ];
1081
1082 let parser = TcpProtocol;
1083 let mut context = ParseContext::new(1);
1084 context.insert_hint("ip_protocol", 6);
1085
1086 let result = parser.parse(&header, &context);
1087
1088 assert!(result.is_ok());
1089 assert_eq!(result.get("options_length"), Some(&FieldValue::UInt8(4)));
1090 assert_eq!(result.get("mss"), Some(&FieldValue::UInt16(1460)));
1091 match result.get("options") {
1093 Some(FieldValue::Str(s)) => assert!(s.contains("MSS")),
1094 Some(FieldValue::OwnedString(s)) => assert!(s.contains("MSS")),
1095 _ => panic!("Expected string for options"),
1096 }
1097 }
1098
1099 #[test]
1100 fn test_parse_tcp_window_scale_option() {
1101 let header = [
1104 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x07, 0x00, ];
1117
1118 let parser = TcpProtocol;
1119 let mut context = ParseContext::new(1);
1120 context.insert_hint("ip_protocol", 6);
1121
1122 let result = parser.parse(&header, &context);
1123
1124 assert!(result.is_ok());
1125 assert_eq!(result.get("window_scale"), Some(&FieldValue::UInt8(7)));
1126 }
1127
1128 #[test]
1129 fn test_parse_tcp_sack_permitted_option() {
1130 let header = [
1133 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x00, ];
1146
1147 let parser = TcpProtocol;
1148 let mut context = ParseContext::new(1);
1149 context.insert_hint("ip_protocol", 6);
1150
1151 let result = parser.parse(&header, &context);
1152
1153 assert!(result.is_ok());
1154 assert_eq!(result.get("sack_permitted"), Some(&FieldValue::Bool(true)));
1155 }
1156
1157 #[test]
1158 fn test_parse_tcp_sack_blocks() {
1159 let header = [
1162 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x80, 0x10, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x0a, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, ];
1179
1180 let parser = TcpProtocol;
1181 let mut context = ParseContext::new(1);
1182 context.insert_hint("ip_protocol", 6);
1183
1184 let result = parser.parse(&header, &context);
1185
1186 assert!(result.is_ok());
1187
1188 if let Some(FieldValue::List(edges)) = result.get("sack_left_edges") {
1190 assert_eq!(edges.len(), 1);
1191 assert_eq!(edges[0], FieldValue::UInt32(4096));
1192 } else {
1193 panic!("Expected List for sack_left_edges");
1194 }
1195
1196 if let Some(FieldValue::List(edges)) = result.get("sack_right_edges") {
1198 assert_eq!(edges.len(), 1);
1199 assert_eq!(edges[0], FieldValue::UInt32(8192));
1200 } else {
1201 panic!("Expected List for sack_right_edges");
1202 }
1203 }
1204
1205 #[test]
1206 fn test_parse_tcp_multiple_options() {
1207 let header = [
1211 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07, ];
1227
1228 let parser = TcpProtocol;
1229 let mut context = ParseContext::new(1);
1230 context.insert_hint("ip_protocol", 6);
1231
1232 let result = parser.parse(&header, &context);
1233
1234 assert!(result.is_ok());
1235 assert_eq!(result.get("mss"), Some(&FieldValue::UInt16(1460)));
1236 assert_eq!(result.get("sack_permitted"), Some(&FieldValue::Bool(true)));
1237 assert_eq!(result.get("window_scale"), Some(&FieldValue::UInt8(7)));
1238 assert_eq!(result.get("ts_val"), Some(&FieldValue::UInt32(1)));
1239 assert_eq!(result.get("ts_ecr"), Some(&FieldValue::UInt32(0)));
1240
1241 let opts_str = match result.get("options") {
1243 Some(FieldValue::Str(s)) => *s,
1244 Some(FieldValue::OwnedString(s)) => s.as_str(),
1245 _ => panic!("Expected string for options"),
1246 };
1247 assert!(
1248 opts_str.contains("MSS"),
1249 "options should contain MSS: {}",
1250 opts_str
1251 );
1252 assert!(
1253 opts_str.contains("SACK_PERM"),
1254 "options should contain SACK_PERM: {}",
1255 opts_str
1256 );
1257 assert!(
1258 opts_str.contains("TS"),
1259 "options should contain TS: {}",
1260 opts_str
1261 );
1262 assert!(
1263 opts_str.contains("WS"),
1264 "options should contain WS: {}",
1265 opts_str
1266 );
1267 }
1268
1269 #[test]
1270 fn test_parse_tcp_timestamp_projected() {
1271 let header = [
1273 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x80, 0x10, 0x72, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44, ];
1290
1291 let parser = TcpProtocol;
1292 let mut context = ParseContext::new(1);
1293 context.insert_hint("ip_protocol", 6);
1294
1295 let fields: HashSet<String> = ["ts_val", "ts_ecr"].iter().map(|s| s.to_string()).collect();
1297 let result = parser.parse_projected(&header, &context, Some(&fields));
1298
1299 assert!(result.is_ok());
1300 assert_eq!(result.get("ts_val"), Some(&FieldValue::UInt32(0xaabbccdd)));
1301 assert_eq!(result.get("ts_ecr"), Some(&FieldValue::UInt32(0x11223344)));
1302 assert!(result.get("src_port").is_none());
1304 assert!(result.get("seq").is_none());
1305 }
1306}