pcapsql_core/protocol/
tcp.rs

1//! TCP protocol parser.
2
3use 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
12/// IP protocol number for TCP.
13pub const IP_PROTO_TCP: u8 = 6;
14
15/// TCP flags bit positions.
16pub 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/// TCP option kinds.
29#[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/// Parsed TCP options container.
41/// Uses zero-copy parsing via etherparse.
42#[derive(Debug, Clone, Default)]
43pub struct ParsedTcpOptions {
44    /// List of option names present (e.g., "MSS,WS,TS")
45    pub option_names: SmallVec<[&'static str; 4]>,
46    /// Maximum Segment Size (option 2)
47    pub mss: Option<u16>,
48    /// Window Scale factor (option 3)
49    pub window_scale: Option<u8>,
50    /// SACK Permitted (option 4)
51    pub sack_permitted: bool,
52    /// SACK block left edges (option 5) - parallel to sack_right_edges
53    pub sack_left_edges: SmallVec<[u32; 4]>,
54    /// SACK block right edges (option 5) - parallel to sack_left_edges
55    pub sack_right_edges: SmallVec<[u32; 4]>,
56    /// Timestamp value (TSval) - sender's timestamp (option 8)
57    pub ts_val: Option<u32>,
58    /// Timestamp echo reply (TSecr) - echoed timestamp from peer (option 8)
59    pub ts_ecr: Option<u32>,
60}
61
62/// Get a static string for common TCP option combinations.
63/// Returns None for uncommon combinations, which fall back to dynamic allocation.
64fn get_options_static(opts: &ParsedTcpOptions) -> Option<&'static str> {
65    // Build bitmask: MSS=1, WS=2, SACK_PERM=4, SACK=8, TS=16
66    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, // Unknown option, fall back to dynamic
75        }
76    }
77
78    // Lookup common combinations (in order options appear in parse_tcp_options)
79    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, // Uncommon combination
97    }
98}
99
100/// Parse TCP options using etherparse's iterator.
101/// This is zero-copy for all options.
102fn 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, // Skip malformed options
109        };
110
111        match opt {
112            TcpOptionElement::Noop => {
113                // NOP - skip, don't add to option names
114            }
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                // Store SACK blocks as parallel lists of left/right edges
129                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/// TCP protocol parser.
149#[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                // Compute flags as a combined value
180                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                // Individual flag fields for convenience
211                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                // Options length
223                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                // Parse all TCP options
228                if options_len > 0 {
229                    let opts = parse_tcp_options(&tcp);
230
231                    // Options list string - use static lookup for common combinations
232                    if !opts.option_names.is_empty() {
233                        let options_str = if let Some(static_str) = get_options_static(&opts) {
234                            FieldValue::Str(static_str) // Zero-copy static string
235                        } 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                    // MSS
244                    if let Some(mss) = opts.mss {
245                        fields.push(("mss", FieldValue::UInt16(mss)));
246                    } else {
247                        fields.push(("mss", FieldValue::Null));
248                    }
249
250                    // Window Scale
251                    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                    // SACK Permitted
258                    if opts.sack_permitted {
259                        fields.push(("sack_permitted", FieldValue::Bool(true)));
260                    } else {
261                        fields.push(("sack_permitted", FieldValue::Null));
262                    }
263
264                    // SACK Blocks as parallel lists
265                    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                    // Timestamps
284                    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                    // No options - all null
296                    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)); // TCP
310
311                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            // TCP options fields
336            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        &[] // Application protocols handled by StreamManager
371    }
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        // If no projection, use full parse
388        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                // Always extract ports for child hints (needed for protocol detection)
400                let src_port = tcp.source_port();
401                let dst_port = tcp.destination_port();
402
403                // Only insert requested fields
404                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                // Compute flags only if any flag field is requested
421                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                // Parse TCP options if any option field is requested
497                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                        // No options - all null
592                        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                // Child hints always needed for protocol chaining
620                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)); // TCP
624
625                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        // These fields are extracted from the fixed header with minimal computation
633        &[
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        // Options require computing data_offset and variable-length parsing
654        &[
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        // Test that common option combinations return static strings
675        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        // Test single options
685        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        // Test uncommon combination returns None
690        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        // TCP SYN packet (20 byte header, no options)
698        let header = [
699            0x00, 0x50, // Src port: 80
700            0x1f, 0x90, // Dst port: 8080
701            0x00, 0x00, 0x00, 0x01, // Seq: 1
702            0x00, 0x00, 0x00, 0x00, // Ack: 0
703            0x50, // Data offset: 5 (20 bytes)
704            0x02, // Flags: SYN
705            0x72, 0x10, // Window: 29200
706            0x00, 0x00, // Checksum
707            0x00, 0x00, // Urgent pointer
708        ];
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, // Src port: 8080
728            0x00, 0x50, // Dst port: 80
729            0x00, 0x00, 0x10, 0x00, // Seq: 4096
730            0x00, 0x00, 0x00, 0x02, // Ack: 2
731            0x50, // Data offset: 5 (20 bytes)
732            0x12, // Flags: SYN + ACK
733            0xff, 0xff, // Window: 65535
734            0x00, 0x00, // Checksum
735            0x00, 0x00, // Urgent pointer
736        ];
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, // Src port: 80
759            0xc0, 0x00, // Dst port: 49152
760            0x00, 0x01, 0x00, 0x00, // Seq
761            0x00, 0x02, 0x00, 0x00, // Ack
762            0x50, // Data offset: 5
763            0x11, // Flags: FIN + ACK
764            0x00, 0x01, // Window: 1
765            0x00, 0x00, // Checksum
766            0x00, 0x00, // Urgent pointer
767        ];
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, // Src port: 80
785            0xc0, 0x00, // Dst port: 49152
786            0x00, 0x00, 0x00, 0x00, // Seq
787            0x00, 0x00, 0x00, 0x00, // Ack
788            0x50, // Data offset: 5
789            0x04, // Flags: RST
790            0x00, 0x00, // Window: 0
791            0x00, 0x00, // Checksum
792            0x00, 0x00, // Urgent pointer
793        ];
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, // Src port: 443
810            0xd4, 0x31, // Dst port: 54321
811            0x00, 0x00, 0x00, 0x01, // Seq
812            0x00, 0x00, 0x00, 0x01, // Ack
813            0x50, // Data offset: 5
814            0x18, // Flags: PSH + ACK
815            0x10, 0x00, // Window: 4096
816            0x00, 0x00, // Checksum
817            0x00, 0x00, // Urgent pointer
818            // Payload
819            0x48, 0x54, 0x54, 0x50, // "HTTP"
820        ];
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); // Payload
834    }
835
836    #[test]
837    fn test_can_parse_tcp() {
838        let parser = TcpProtocol;
839
840        // Without hint
841        let ctx1 = ParseContext::new(1);
842        assert!(parser.can_parse(&ctx1).is_none());
843
844        // With wrong protocol
845        let mut ctx2 = ParseContext::new(1);
846        ctx2.insert_hint("ip_protocol", 17); // UDP
847        assert!(parser.can_parse(&ctx2).is_none());
848
849        // With TCP protocol
850        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]; // Only 4 bytes
858
859        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, // Src port: 443
873            0x00, 0x50, // Dst port: 80
874            0x00, 0x00, 0x00, 0x01, // Seq
875            0x00, 0x00, 0x00, 0x00, // Ack
876            0x50, // Data offset: 5
877            0x02, // Flags: SYN
878            0xff, 0xff, // Window
879            0x00, 0x00, // Checksum
880            0x00, 0x00, // Urgent pointer
881        ];
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, // Src port: 80
899            0x1f, 0x90, // Dst port: 8080
900            0x00, 0x00, 0x00, 0x01, // Seq: 1
901            0x00, 0x00, 0x00, 0x00, // Ack: 0
902            0x50, // Data offset: 5 (20 bytes)
903            0x02, // Flags: SYN
904            0x72, 0x10, // Window: 29200
905            0x00, 0x00, // Checksum
906            0x00, 0x00, // Urgent pointer
907        ];
908
909        let parser = TcpProtocol;
910        let mut context = ParseContext::new(1);
911        context.insert_hint("ip_protocol", 6);
912
913        // Project to only ports
914        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        // Requested fields are present
922        assert_eq!(result.get("src_port"), Some(&FieldValue::UInt16(80)));
923        assert_eq!(result.get("dst_port"), Some(&FieldValue::UInt16(8080)));
924        // Unrequested fields are NOT present
925        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        // Child hints are still populated
930        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, // Src port: 80
938            0x1f, 0x90, // Dst port: 8080
939            0x00, 0x00, 0x00, 0x01, // Seq: 1
940            0x00, 0x00, 0x00, 0x00, // Ack: 0
941            0x50, // Data offset: 5 (20 bytes)
942            0x12, // Flags: SYN + ACK
943            0x72, 0x10, // Window: 29200
944            0x00, 0x00, // Checksum
945            0x00, 0x00, // Urgent pointer
946        ];
947
948        let parser = TcpProtocol;
949        let mut context = ParseContext::new(1);
950        context.insert_hint("ip_protocol", 6);
951
952        // Project to ports and specific flags
953        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        // Combined flags field not requested
964        assert!(result.get("flags").is_none());
965        // Other fields not requested
966        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, // Src port: 80
974            0x1f, 0x90, // Dst port: 8080
975            0x00, 0x00, 0x00, 0x01, // Seq: 1
976            0x00, 0x00, 0x00, 0x00, // Ack: 0
977            0x50, // Data offset: 5 (20 bytes)
978            0x02, // Flags: SYN
979            0x72, 0x10, // Window: 29200
980            0x00, 0x00, // Checksum
981            0x00, 0x00, // Urgent pointer
982        ];
983
984        let parser = TcpProtocol;
985        let mut context = ParseContext::new(1);
986        context.insert_hint("ip_protocol", 6);
987
988        // No projection - should return all fields
989        let result = parser.parse_projected(&header, &context, None);
990
991        assert!(result.is_ok());
992        // All fields present
993        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        // TCP packet with timestamp option
1005        // Header: 32 bytes (20 base + 12 options)
1006        let header = [
1007            0x00, 0x50, // Src port: 80
1008            0x1f, 0x90, // Dst port: 8080
1009            0x00, 0x00, 0x00, 0x01, // Seq: 1
1010            0x00, 0x00, 0x00, 0x02, // Ack: 2
1011            0x80, // Data offset: 8 (32 bytes)
1012            0x10, // Flags: ACK
1013            0x72, 0x10, // Window: 29200
1014            0x00, 0x00, // Checksum
1015            0x00, 0x00, // Urgent pointer
1016            // TCP Options (12 bytes):
1017            0x01, // NOP
1018            0x01, // NOP
1019            0x08, // Timestamp option kind
1020            0x0a, // Timestamp option length (10)
1021            0x12, 0x34, 0x56, 0x78, // TSval: 0x12345678 = 305419896
1022            0x9a, 0xbc, 0xde, 0xf0, // TSecr: 0x9abcdef0 = 2596069104
1023        ];
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        // TCP packet without timestamp option (20 byte header)
1040        let header = [
1041            0x00, 0x50, // Src port: 80
1042            0x1f, 0x90, // Dst port: 8080
1043            0x00, 0x00, 0x00, 0x01, // Seq: 1
1044            0x00, 0x00, 0x00, 0x00, // Ack: 0
1045            0x50, // Data offset: 5 (20 bytes)
1046            0x02, // Flags: SYN
1047            0x72, 0x10, // Window: 29200
1048            0x00, 0x00, // Checksum
1049            0x00, 0x00, // Urgent pointer
1050        ];
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        // TCP SYN packet with MSS option
1067        // Header: 24 bytes (20 base + 4 options)
1068        let header = [
1069            0x00, 0x50, // Src port: 80
1070            0x1f, 0x90, // Dst port: 8080
1071            0x00, 0x00, 0x00, 0x01, // Seq: 1
1072            0x00, 0x00, 0x00, 0x00, // Ack: 0
1073            0x60, // Data offset: 6 (24 bytes)
1074            0x02, // Flags: SYN
1075            0x72, 0x10, // Window: 29200
1076            0x00, 0x00, // Checksum
1077            0x00, 0x00, // Urgent pointer
1078            // TCP Options (4 bytes):
1079            0x02, 0x04, 0x05, 0xb4, // MSS = 1460
1080        ];
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        // Check options string contains MSS (may be static or owned)
1092        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        // TCP SYN packet with Window Scale option
1102        // Header: 24 bytes (20 base + 4 options, padded)
1103        let header = [
1104            0x00, 0x50, // Src port: 80
1105            0x1f, 0x90, // Dst port: 8080
1106            0x00, 0x00, 0x00, 0x01, // Seq: 1
1107            0x00, 0x00, 0x00, 0x00, // Ack: 0
1108            0x60, // Data offset: 6 (24 bytes)
1109            0x02, // Flags: SYN
1110            0x72, 0x10, // Window: 29200
1111            0x00, 0x00, // Checksum
1112            0x00, 0x00, // Urgent pointer
1113            // TCP Options (4 bytes):
1114            0x03, 0x03, 0x07, // Window Scale = 7
1115            0x00, // Padding (End of options)
1116        ];
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        // TCP SYN packet with SACK Permitted option
1131        // Header: 24 bytes (20 base + 4 options, padded)
1132        let header = [
1133            0x00, 0x50, // Src port: 80
1134            0x1f, 0x90, // Dst port: 8080
1135            0x00, 0x00, 0x00, 0x01, // Seq: 1
1136            0x00, 0x00, 0x00, 0x00, // Ack: 0
1137            0x60, // Data offset: 6 (24 bytes)
1138            0x02, // Flags: SYN
1139            0x72, 0x10, // Window: 29200
1140            0x00, 0x00, // Checksum
1141            0x00, 0x00, // Urgent pointer
1142            // TCP Options (4 bytes):
1143            0x04, 0x02, // SACK Permitted
1144            0x01, 0x00, // NOP + padding
1145        ];
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        // TCP packet with SACK blocks (during retransmission)
1160        // Header: 32 bytes (20 base + 12 options)
1161        let header = [
1162            0x00, 0x50, // Src port: 80
1163            0x1f, 0x90, // Dst port: 8080
1164            0x00, 0x00, 0x00, 0x01, // Seq: 1
1165            0x00, 0x00, 0x00, 0x02, // Ack: 2
1166            0x80, // Data offset: 8 (32 bytes)
1167            0x10, // Flags: ACK
1168            0x72, 0x10, // Window: 29200
1169            0x00, 0x00, // Checksum
1170            0x00, 0x00, // Urgent pointer
1171            // TCP Options (12 bytes):
1172            0x01, // NOP
1173            0x01, // NOP
1174            // SACK option with 1 block
1175            0x05, 0x0a, // SACK, length 10 (2 + 8 bytes)
1176            0x00, 0x00, 0x10, 0x00, // Left edge: 4096
1177            0x00, 0x00, 0x20, 0x00, // Right edge: 8192
1178        ];
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        // Check SACK left edges
1189        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        // Check SACK right edges
1197        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        // TCP SYN packet with MSS, SACK Permitted, Timestamps, Window Scale
1208        // This is a typical SYN packet with all common options
1209        // Header: 40 bytes (20 base + 20 options)
1210        let header = [
1211            0x00, 0x50, // Src port: 80
1212            0x1f, 0x90, // Dst port: 8080
1213            0x00, 0x00, 0x00, 0x01, // Seq: 1
1214            0x00, 0x00, 0x00, 0x00, // Ack: 0
1215            0xa0, // Data offset: 10 (40 bytes)
1216            0x02, // Flags: SYN
1217            0x72, 0x10, // Window: 29200
1218            0x00, 0x00, // Checksum
1219            0x00, 0x00, // Urgent pointer
1220            // TCP Options (20 bytes):
1221            0x02, 0x04, 0x05, 0xb4, // MSS 1460
1222            0x04, 0x02, // SACK Permitted
1223            0x08, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // Timestamps
1224            0x01, // NOP
1225            0x03, 0x03, 0x07, // Window Scale 7
1226        ];
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        // Check options string contains all options (may be static or owned)
1242        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        // TCP packet with timestamp option
1272        let header = [
1273            0x00, 0x50, // Src port: 80
1274            0x1f, 0x90, // Dst port: 8080
1275            0x00, 0x00, 0x00, 0x01, // Seq: 1
1276            0x00, 0x00, 0x00, 0x02, // Ack: 2
1277            0x80, // Data offset: 8 (32 bytes)
1278            0x10, // Flags: ACK
1279            0x72, 0x10, // Window: 29200
1280            0x00, 0x00, // Checksum
1281            0x00, 0x00, // Urgent pointer
1282            // TCP Options:
1283            0x01, // NOP
1284            0x01, // NOP
1285            0x08, // Timestamp option kind
1286            0x0a, // Timestamp option length
1287            0xaa, 0xbb, 0xcc, 0xdd, // TSval
1288            0x11, 0x22, 0x33, 0x44, // TSecr
1289        ];
1290
1291        let parser = TcpProtocol;
1292        let mut context = ParseContext::new(1);
1293        context.insert_hint("ip_protocol", 6);
1294
1295        // Project to only timestamp fields
1296        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        // Other fields not present
1303        assert!(result.get("src_port").is_none());
1304        assert!(result.get("seq").is_none());
1305    }
1306}