nex_packet/
tcp.rs

1//! A TCP packet abstraction.
2
3use crate::ip::IpNextProtocol;
4use crate::packet::Packet;
5
6use crate::util::{self, Octets};
7use std::net::Ipv6Addr;
8use std::net::{IpAddr, Ipv4Addr};
9
10use bytes::{Buf, BufMut, Bytes, BytesMut};
11use nex_core::bitfield::{u16be, u32be, u4};
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15/// Minimum TCP Header Length
16pub const TCP_HEADER_LEN: usize = 20;
17/// Minimum TCP Data Offset
18pub const TCP_MIN_DATA_OFFSET: u8 = 5;
19/// Maximum TCP Option Length
20pub const TCP_OPTION_MAX_LEN: usize = 40;
21/// Maximum TCP Header Length (with options)
22pub const TCP_HEADER_MAX_LEN: usize = TCP_HEADER_LEN + TCP_OPTION_MAX_LEN;
23
24/// Represents a TCP Option Kind.
25/// <https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-parameters-1>
26#[allow(non_camel_case_types)]
27#[repr(u8)]
28#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30pub enum TcpOptionKind {
31    EOL = 0,
32    NOP = 1,
33    MSS = 2,
34    WSCALE = 3,
35    SACK_PERMITTED = 4,
36    SACK = 5,
37    ECHO = 6,
38    ECHO_REPLY = 7,
39    TIMESTAMPS = 8,
40    POCP = 9,
41    POSP = 10,
42    CC = 11,
43    CC_NEW = 12,
44    CC_ECHO = 13,
45    ALT_CHECKSUM_REQ = 14,
46    ALT_CHECKSUM_DATA = 15,
47    SKEETER = 16,
48    BUBBA = 17,
49    TRAILER_CHECKSUM = 18,
50    MD5_SIGNATURE = 19,
51    SCPS_CAPABILITIES = 20,
52    SELECTIVE_ACK = 21,
53    RECORD_BOUNDARIES = 22,
54    CORRUPTION_EXPERIENCED = 23,
55    SNAP = 24,
56    UNASSIGNED = 25,
57    TCP_COMPRESSION_FILTER = 26,
58    QUICK_START = 27,
59    USER_TIMEOUT = 28,
60    TCP_AO = 29,
61    MPTCP = 30,
62    RESERVED_31 = 31,
63    RESERVED_32 = 32,
64    RESERVED_33 = 33,
65    FAST_OPEN_COOKIE = 34,
66    TCP_ENO = 69,
67    ACC_ECNO_0 = 172,
68    ACC_ECNO_1 = 174,
69    EXPERIMENT_1 = 253,
70    EXPERIMENT_2 = 254,
71    RESERVED(u8),
72}
73
74impl TcpOptionKind {
75    /// Construct a TCP option kind from a u8.
76    pub fn new(n: u8) -> TcpOptionKind {
77        match n {
78            0 => TcpOptionKind::EOL,
79            1 => TcpOptionKind::NOP,
80            2 => TcpOptionKind::MSS,
81            3 => TcpOptionKind::WSCALE,
82            4 => TcpOptionKind::SACK_PERMITTED,
83            5 => TcpOptionKind::SACK,
84            6 => TcpOptionKind::ECHO,
85            7 => TcpOptionKind::ECHO_REPLY,
86            8 => TcpOptionKind::TIMESTAMPS,
87            9 => TcpOptionKind::POCP,
88            10 => TcpOptionKind::POSP,
89            11 => TcpOptionKind::CC,
90            12 => TcpOptionKind::CC_NEW,
91            13 => TcpOptionKind::CC_ECHO,
92            14 => TcpOptionKind::ALT_CHECKSUM_REQ,
93            15 => TcpOptionKind::ALT_CHECKSUM_DATA,
94            16 => TcpOptionKind::SKEETER,
95            17 => TcpOptionKind::BUBBA,
96            18 => TcpOptionKind::TRAILER_CHECKSUM,
97            19 => TcpOptionKind::MD5_SIGNATURE,
98            20 => TcpOptionKind::SCPS_CAPABILITIES,
99            21 => TcpOptionKind::SELECTIVE_ACK,
100            22 => TcpOptionKind::RECORD_BOUNDARIES,
101            23 => TcpOptionKind::CORRUPTION_EXPERIENCED,
102            24 => TcpOptionKind::SNAP,
103            25 => TcpOptionKind::UNASSIGNED,
104            26 => TcpOptionKind::TCP_COMPRESSION_FILTER,
105            27 => TcpOptionKind::QUICK_START,
106            28 => TcpOptionKind::USER_TIMEOUT,
107            29 => TcpOptionKind::TCP_AO,
108            30 => TcpOptionKind::MPTCP,
109            31 => TcpOptionKind::RESERVED_31,
110            32 => TcpOptionKind::RESERVED_32,
111            33 => TcpOptionKind::RESERVED_33,
112            34 => TcpOptionKind::FAST_OPEN_COOKIE,
113            69 => TcpOptionKind::TCP_ENO,
114            172 => TcpOptionKind::ACC_ECNO_0,
115            174 => TcpOptionKind::ACC_ECNO_1,
116            253 => TcpOptionKind::EXPERIMENT_1,
117            254 => TcpOptionKind::EXPERIMENT_2,
118            _ => TcpOptionKind::RESERVED(n),
119        }
120    }
121
122    /// Get the name of the TCP option kind.
123    pub fn name(&self) -> &'static str {
124        match *self {
125            TcpOptionKind::EOL => "EOL",
126            TcpOptionKind::NOP => "NOP",
127            TcpOptionKind::MSS => "MSS",
128            TcpOptionKind::WSCALE => "WSCALE",
129            TcpOptionKind::SACK_PERMITTED => "SACK_PERMITTED",
130            TcpOptionKind::SACK => "SACK",
131            TcpOptionKind::ECHO => "ECHO",
132            TcpOptionKind::ECHO_REPLY => "ECHO_REPLY",
133            TcpOptionKind::TIMESTAMPS => "TIMESTAMPS",
134            TcpOptionKind::POCP => "POCP",
135            TcpOptionKind::POSP => "POSP",
136            TcpOptionKind::CC => "CC",
137            TcpOptionKind::CC_NEW => "CC_NEW",
138            TcpOptionKind::CC_ECHO => "CC_ECHO",
139            TcpOptionKind::ALT_CHECKSUM_REQ => "ALT_CHECKSUM_REQ",
140            TcpOptionKind::ALT_CHECKSUM_DATA => "ALT_CHECKSUM_DATA",
141            TcpOptionKind::SKEETER => "SKEETER",
142            TcpOptionKind::BUBBA => "BUBBA",
143            TcpOptionKind::TRAILER_CHECKSUM => "TRAILER_CHECKSUM",
144            TcpOptionKind::MD5_SIGNATURE => "MD5_SIGNATURE",
145            TcpOptionKind::SCPS_CAPABILITIES => "SCPS_CAPABILITIES",
146            TcpOptionKind::SELECTIVE_ACK => "SELECTIVE_ACK",
147            TcpOptionKind::RECORD_BOUNDARIES => "RECORD_BOUNDARIES",
148            TcpOptionKind::CORRUPTION_EXPERIENCED => "CORRUPTION_EXPERIENCED",
149            TcpOptionKind::SNAP => "SNAP",
150            TcpOptionKind::UNASSIGNED => "UNASSIGNED",
151            TcpOptionKind::TCP_COMPRESSION_FILTER => "TCP_COMPRESSION_FILTER",
152            TcpOptionKind::QUICK_START => "QUICK_START",
153            TcpOptionKind::USER_TIMEOUT => "USER_TIMEOUT",
154            TcpOptionKind::TCP_AO => "TCP_AO",
155            TcpOptionKind::MPTCP => "MPTCP",
156            TcpOptionKind::RESERVED_31 => "RESERVED_31",
157            TcpOptionKind::RESERVED_32 => "RESERVED_32",
158            TcpOptionKind::RESERVED_33 => "RESERVED_33",
159            TcpOptionKind::FAST_OPEN_COOKIE => "FAST_OPEN_COOKIE",
160            TcpOptionKind::TCP_ENO => "TCP_ENO",
161            TcpOptionKind::ACC_ECNO_0 => "ACC_ECNO_0",
162            TcpOptionKind::ACC_ECNO_1 => "ACC_ECNO_1",
163            TcpOptionKind::EXPERIMENT_1 => "EXPERIMENT_1",
164            TcpOptionKind::EXPERIMENT_2 => "EXPERIMENT_2",
165            TcpOptionKind::RESERVED(_) => "RESERVED",
166        }
167    }
168    /// Get the value of the TCP option kind.
169    pub fn value(&self) -> u8 {
170        match *self {
171            TcpOptionKind::EOL => 0,
172            TcpOptionKind::NOP => 1,
173            TcpOptionKind::MSS => 2,
174            TcpOptionKind::WSCALE => 3,
175            TcpOptionKind::SACK_PERMITTED => 4,
176            TcpOptionKind::SACK => 5,
177            TcpOptionKind::ECHO => 6,
178            TcpOptionKind::ECHO_REPLY => 7,
179            TcpOptionKind::TIMESTAMPS => 8,
180            TcpOptionKind::POCP => 9,
181            TcpOptionKind::POSP => 10,
182            TcpOptionKind::CC => 11,
183            TcpOptionKind::CC_NEW => 12,
184            TcpOptionKind::CC_ECHO => 13,
185            TcpOptionKind::ALT_CHECKSUM_REQ => 14,
186            TcpOptionKind::ALT_CHECKSUM_DATA => 15,
187            TcpOptionKind::SKEETER => 16,
188            TcpOptionKind::BUBBA => 17,
189            TcpOptionKind::TRAILER_CHECKSUM => 18,
190            TcpOptionKind::MD5_SIGNATURE => 19,
191            TcpOptionKind::SCPS_CAPABILITIES => 20,
192            TcpOptionKind::SELECTIVE_ACK => 21,
193            TcpOptionKind::RECORD_BOUNDARIES => 22,
194            TcpOptionKind::CORRUPTION_EXPERIENCED => 23,
195            TcpOptionKind::SNAP => 24,
196            TcpOptionKind::UNASSIGNED => 25,
197            TcpOptionKind::TCP_COMPRESSION_FILTER => 26,
198            TcpOptionKind::QUICK_START => 27,
199            TcpOptionKind::USER_TIMEOUT => 28,
200            TcpOptionKind::TCP_AO => 29,
201            TcpOptionKind::MPTCP => 30,
202            TcpOptionKind::RESERVED_31 => 31,
203            TcpOptionKind::RESERVED_32 => 32,
204            TcpOptionKind::RESERVED_33 => 33,
205            TcpOptionKind::FAST_OPEN_COOKIE => 34,
206            TcpOptionKind::TCP_ENO => 69,
207            TcpOptionKind::ACC_ECNO_0 => 172,
208            TcpOptionKind::ACC_ECNO_1 => 174,
209            TcpOptionKind::EXPERIMENT_1 => 253,
210            TcpOptionKind::EXPERIMENT_2 => 254,
211            TcpOptionKind::RESERVED(n) => n,
212        }
213    }
214    /// Get size (bytes) of the TCP option.
215    pub fn size(&self) -> usize {
216        match *self {
217            TcpOptionKind::EOL => 1,
218            TcpOptionKind::NOP => 1,
219            TcpOptionKind::MSS => 4,
220            TcpOptionKind::WSCALE => 3,
221            TcpOptionKind::SACK_PERMITTED => 2,
222            TcpOptionKind::SACK => 10,
223            TcpOptionKind::ECHO => 6,
224            TcpOptionKind::ECHO_REPLY => 6,
225            TcpOptionKind::TIMESTAMPS => 10,
226            TcpOptionKind::POCP => 2,
227            TcpOptionKind::POSP => 3,
228            TcpOptionKind::ALT_CHECKSUM_REQ => 3,
229            TcpOptionKind::ALT_CHECKSUM_DATA => 12,
230            TcpOptionKind::TRAILER_CHECKSUM => 3,
231            TcpOptionKind::MD5_SIGNATURE => 18,
232            TcpOptionKind::QUICK_START => 8,
233            TcpOptionKind::USER_TIMEOUT => 4,
234            _ => 0,
235        }
236    }
237}
238
239/// Represents the TCP Flags
240/// <https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-header-flags>
241#[allow(non_snake_case)]
242#[allow(non_upper_case_globals)]
243pub mod TcpFlags {
244    /// CWR – Congestion Window Reduced (CWR) flag is set by the sending
245    /// host to indicate that it received a TCP segment with the ECE flag set
246    /// and had responded in congestion control mechanism.
247    pub const CWR: u8 = 0b10000000;
248    /// ECE – ECN-Echo has a dual role, depending on the value of the
249    /// SYN flag. It indicates:
250    /// If the SYN flag is set (1), that the TCP peer is ECN capable.
251    /// If the SYN flag is clear (0), that a packet with Congestion Experienced
252    /// flag set (ECN=11) in IP header received during normal transmission.
253    pub const ECE: u8 = 0b01000000;
254    /// URG – indicates that the Urgent pointer field is significant.
255    pub const URG: u8 = 0b00100000;
256    /// ACK – indicates that the Acknowledgment field is significant.
257    /// All packets after the initial SYN packet sent by the client should have this flag set.
258    pub const ACK: u8 = 0b00010000;
259    /// PSH – Push function. Asks to push the buffered data to the receiving application.
260    pub const PSH: u8 = 0b00001000;
261    /// RST – Reset the connection.
262    pub const RST: u8 = 0b00000100;
263    /// SYN – Synchronize sequence numbers. Only the first packet sent from each end
264    /// should have this flag set.
265    pub const SYN: u8 = 0b00000010;
266    /// FIN – No more data from sender.
267    pub const FIN: u8 = 0b00000001;
268}
269
270/// Represents the TCP option header.
271#[derive(Clone, Debug, PartialEq, Eq)]
272#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
273pub struct TcpOptionHeader {
274    pub kind: TcpOptionKind,
275    pub length: Option<u8>,
276    pub data: Bytes,
277}
278
279impl TcpOptionHeader {
280    /// Get the timestamp of the TCP option
281    pub fn get_timestamp(&self) -> (u32, u32) {
282        if self.kind == TcpOptionKind::TIMESTAMPS && self.data.len() >= 8 {
283            let mut my: [u8; 4] = [0; 4];
284            my.copy_from_slice(&self.data[0..4]);
285            let mut their: [u8; 4] = [0; 4];
286            their.copy_from_slice(&self.data[4..8]);
287            (u32::from_be_bytes(my), u32::from_be_bytes(their))
288        } else {
289            return (0, 0);
290        }
291    }
292    /// Get the MSS of the TCP option
293    pub fn get_mss(&self) -> u16 {
294        if self.kind == TcpOptionKind::MSS && self.data.len() >= 2 {
295            let mut mss: [u8; 2] = [0; 2];
296            mss.copy_from_slice(&self.data[0..2]);
297            u16::from_be_bytes(mss)
298        } else {
299            0
300        }
301    }
302    /// Get the WSCALE of the TCP option
303    pub fn get_wscale(&self) -> u8 {
304        if self.kind == TcpOptionKind::WSCALE && self.data.len() > 0 {
305            self.data[0]
306        } else {
307            0
308        }
309    }
310}
311
312/// A TCP option.
313#[derive(Clone, Debug, PartialEq, Eq)]
314#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
315pub struct TcpOptionPacket {
316    kind: TcpOptionKind,
317    length: Option<u8>,
318    data: Bytes,
319}
320
321impl TcpOptionPacket {
322    /// NOP: This may be used to align option fields on 32-bit boundaries for better performance.
323    pub fn nop() -> Self {
324        TcpOptionPacket {
325            kind: TcpOptionKind::NOP,
326            length: None,
327            data: Bytes::new(),
328        }
329    }
330
331    /// Timestamp: TCP timestamps, defined in RFC 1323, can help TCP determine in which order
332    /// packets were sent. TCP timestamps are not normally aligned to the system clock and
333    /// start at some random value.
334    pub fn timestamp(my: u32, their: u32) -> Self {
335        let mut data = BytesMut::new();
336        data.extend_from_slice(&my.octets()[..]);
337        data.extend_from_slice(&their.octets()[..]);
338
339        TcpOptionPacket {
340            kind: TcpOptionKind::TIMESTAMPS,
341            length: Some(10),
342            data: data.freeze(),
343        }
344    }
345
346    /// MSS: The maximum segment size (MSS) is the largest amount of data, specified in bytes,
347    /// that TCP is willing to receive in a single segment.
348    pub fn mss(val: u16) -> Self {
349        let mut data = BytesMut::new();
350        data.extend_from_slice(&val.octets()[..]);
351
352        TcpOptionPacket {
353            kind: TcpOptionKind::MSS,
354            length: Some(4),
355            data: data.freeze(),
356        }
357    }
358
359    /// Window scale: The TCP window scale option, as defined in RFC 1323, is an option used to
360    /// increase the maximum window size from 65,535 bytes to 1 gigabyte.
361    pub fn wscale(val: u8) -> Self {
362        TcpOptionPacket {
363            kind: TcpOptionKind::WSCALE,
364            length: Some(3),
365            data: Bytes::from(vec![val]),
366        }
367    }
368
369    /// Selective acknowledgment (SACK) option, defined in RFC 2018 allows the receiver to acknowledge
370    /// discontinuous blocks of packets which were received correctly. This options enables use of
371    /// SACK during negotiation.
372    pub fn sack_perm() -> Self {
373        TcpOptionPacket {
374            kind: TcpOptionKind::SACK_PERMITTED,
375            length: Some(2),
376            data: Bytes::new(),
377        }
378    }
379
380    /// Selective acknowledgment (SACK) option, defined in RFC 2018 allows the receiver to acknowledge
381    /// discontinuous blocks of packets which were received correctly. The acknowledgement can specify
382    /// a number of SACK blocks, where each SACK block is conveyed by the starting and ending sequence
383    /// numbers of a contiguous range that the receiver correctly received.
384    pub fn selective_ack(acks: &[u32]) -> Self {
385        let mut data = BytesMut::new();
386        for ack in acks {
387            data.extend_from_slice(&ack.octets()[..]);
388        }
389        TcpOptionPacket {
390            kind: TcpOptionKind::SACK,
391            length: Some(1 /* number */ + 1 /* length */ + data.len() as u8),
392            data: data.freeze(),
393        }
394    }
395    /// Get the TCP option kind.
396    pub fn kind(&self) -> TcpOptionKind {
397        self.kind
398    }
399    /// Get length of the TCP option.
400    pub fn length(&self) -> u8 {
401        if let Some(len) = self.length {
402            len
403        } else {
404            // If length is None, it means the option has no length (like NOP).
405            0
406        }
407    }
408    /// Get the timestamp of the TCP option
409    pub fn get_timestamp(&self) -> (u32, u32) {
410        if self.kind == TcpOptionKind::TIMESTAMPS && self.data.len() >= 8 {
411            let mut my: [u8; 4] = [0; 4];
412            my.copy_from_slice(&self.data[0..4]);
413            let mut their: [u8; 4] = [0; 4];
414            their.copy_from_slice(&self.data[4..8]);
415            (u32::from_be_bytes(my), u32::from_be_bytes(their))
416        } else {
417            return (0, 0);
418        }
419    }
420    /// Get the MSS of the TCP option
421    pub fn get_mss(&self) -> u16 {
422        if self.kind == TcpOptionKind::MSS && self.data.len() >= 2 {
423            let mut mss: [u8; 2] = [0; 2];
424            mss.copy_from_slice(&self.data[0..2]);
425            u16::from_be_bytes(mss)
426        } else {
427            0
428        }
429    }
430    /// Get the WSCALE of the TCP option
431    pub fn get_wscale(&self) -> u8 {
432        if self.kind == TcpOptionKind::WSCALE && self.data.len() > 0 {
433            self.data[0]
434        } else {
435            0
436        }
437    }
438}
439
440/// Represents the TCP header.
441#[derive(Clone, Debug, PartialEq, Eq)]
442#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
443pub struct TcpHeader {
444    pub source: u16be,
445    pub destination: u16be,
446    pub sequence: u32be,
447    pub acknowledgement: u32be,
448    pub data_offset: u4,
449    pub reserved: u4,
450    pub flags: u8,
451    pub window: u16be,
452    pub checksum: u16be,
453    pub urgent_ptr: u16be,
454    pub options: Vec<TcpOptionPacket>,
455}
456
457/// Represents a TCP packet.
458#[derive(Clone, Debug, PartialEq, Eq)]
459pub struct TcpPacket {
460    pub header: TcpHeader,
461    pub payload: Bytes,
462}
463
464impl Packet for TcpPacket {
465    type Header = TcpHeader;
466
467    fn from_buf(mut bytes: &[u8]) -> Option<Self> {
468        if bytes.len() < TCP_HEADER_LEN {
469            return None;
470        }
471
472        let source = bytes.get_u16();
473        let destination = bytes.get_u16();
474        let sequence = bytes.get_u32();
475        let acknowledgement = bytes.get_u32();
476
477        let offset_reserved = bytes.get_u8();
478        let data_offset = offset_reserved >> 4;
479        let reserved = offset_reserved & 0x0F;
480
481        let flags = bytes.get_u8();
482        let window = bytes.get_u16();
483        let checksum = bytes.get_u16();
484        let urgent_ptr = bytes.get_u16();
485
486        let header_len = data_offset as usize * 4;
487        if header_len < TCP_HEADER_LEN || bytes.len() + 20 < header_len {
488            return None;
489        }
490
491        let mut options = Vec::new();
492        let options_len = header_len - TCP_HEADER_LEN;
493        let (mut options_bytes, rest) = bytes.split_at(options_len);
494        bytes = rest;
495
496        while options_bytes.has_remaining() {
497            let kind = TcpOptionKind::new(options_bytes.get_u8());
498            match kind {
499                TcpOptionKind::EOL => {
500                    options.push(TcpOptionPacket {
501                        kind,
502                        length: None,
503                        data: Bytes::new(),
504                    });
505                    break;
506                }
507                TcpOptionKind::NOP => {
508                    options.push(TcpOptionPacket {
509                        kind,
510                        length: None,
511                        data: Bytes::new(),
512                    });
513                }
514                _ => {
515                    if options_bytes.remaining() < 1 {
516                        return None;
517                    }
518                    let len = options_bytes.get_u8();
519                    if len < 2 || (len as usize) > options_bytes.remaining() + 2 {
520                        return None;
521                    }
522                    let data_len = (len - 2) as usize;
523                    let (data_slice, rest) = options_bytes.split_at(data_len);
524                    options_bytes = rest;
525                    options.push(TcpOptionPacket {
526                        kind,
527                        length: Some(len),
528                        data: Bytes::copy_from_slice(data_slice),
529                    });
530                }
531            }
532        }
533
534        Some(TcpPacket {
535            header: TcpHeader {
536                source,
537                destination,
538                sequence,
539                acknowledgement,
540                data_offset: u4::from_be(data_offset),
541                reserved: u4::from_be(reserved),
542                flags,
543                window,
544                checksum,
545                urgent_ptr,
546                options,
547            },
548            payload: Bytes::copy_from_slice(bytes),
549        })
550    }
551    fn from_bytes(mut bytes: Bytes) -> Option<Self> {
552        Self::from_buf(&mut bytes)
553    }
554
555    fn to_bytes(&self) -> Bytes {
556        // Calculate the actual encoded length of TCP options
557        let mut enc_opt_len = 0usize;
558        for opt in &self.header.options {
559            match opt.kind {
560                TcpOptionKind::EOL | TcpOptionKind::NOP => enc_opt_len += 1,
561                _ => {
562                    // Total length including kind + length fields
563                    let len = opt.length.unwrap_or(2) as usize;
564                    enc_opt_len += len;
565                }
566            }
567        }
568        // Round up to the nearest 4-byte boundary
569        let padded_opt_len = (enc_opt_len + 3) & !3;
570        let header_len = TCP_HEADER_LEN + padded_opt_len;
571        // In 32-bit words
572        let data_offset_words = (header_len / 4) as u8;
573
574        // Write the TCP header
575        let mut bytes = BytesMut::with_capacity(header_len + self.payload.len());
576        bytes.put_u16(self.header.source);
577        bytes.put_u16(self.header.destination);
578        bytes.put_u32(self.header.sequence);
579        bytes.put_u32(self.header.acknowledgement);
580
581        let offset_reserved = (data_offset_words << 4) | (self.header.reserved.to_be() & 0x0F);
582        bytes.put_u8(offset_reserved);
583
584        bytes.put_u8(self.header.flags);
585        bytes.put_u16(self.header.window);
586        bytes.put_u16(self.header.checksum);
587        bytes.put_u16(self.header.urgent_ptr);
588
589        // Encode the options
590        let before_opts = bytes.len();
591        for opt in &self.header.options {
592            bytes.put_u8(opt.kind.value());
593            if let Some(length) = opt.length {
594                bytes.put_u8(length);
595                bytes.extend_from_slice(&opt.data);
596            }
597        }
598        // Add option padding (zero-filled) to reach the padded length
599        let written_opt = bytes.len() - before_opts;
600        let pad = padded_opt_len.saturating_sub(written_opt);
601        for _ in 0..pad {
602            bytes.put_u8(0);
603        }
604
605        // Append payload
606        bytes.extend_from_slice(&self.payload);
607
608        bytes.freeze()
609    }
610
611    fn header(&self) -> Bytes {
612        self.to_bytes().slice(..self.header_len())
613    }
614
615    fn payload(&self) -> Bytes {
616        self.payload.clone()
617    }
618
619    fn header_len(&self) -> usize {
620        let base = TCP_HEADER_LEN;
621        let mut opt_len = 0;
622
623        for opt in &self.header.options {
624            match opt.kind {
625                TcpOptionKind::EOL | TcpOptionKind::NOP => {
626                    opt_len += 1; // EOL and NOP are one byte
627                }
628                _ => {
629                    // kind(1B) + length(1B) + payload
630                    if let Some(len) = opt.length {
631                        opt_len += len as usize;
632                    } else {
633                        // Ensure at least 2 bytes (kind + length)
634                        opt_len += 2;
635                    }
636                }
637            }
638        }
639
640        let total = base + opt_len;
641        // The TCP header is always rounded to a 4 byte boundary
642        (total + 3) & !0x03
643    }
644
645    fn payload_len(&self) -> usize {
646        self.payload.len()
647    }
648
649    fn total_len(&self) -> usize {
650        self.header_len() + self.payload_len()
651    }
652
653    fn into_parts(self) -> (Self::Header, Bytes) {
654        (self.header, self.payload)
655    }
656}
657
658impl TcpPacket {
659    pub fn tcp_options_length(&self) -> usize {
660        if self.header.data_offset > 5 {
661            self.header.data_offset as usize * 4 - 20
662        } else {
663            0
664        }
665    }
666}
667
668pub fn checksum(packet: &TcpPacket, source: &IpAddr, destination: &IpAddr) -> u16 {
669    match (source, destination) {
670        (IpAddr::V4(src), IpAddr::V4(dst)) => ipv4_checksum(packet, src, dst),
671        (IpAddr::V6(src), IpAddr::V6(dst)) => ipv6_checksum(packet, src, dst),
672        _ => 0, // Unsupported IP version
673    }
674}
675
676/// Calculate a checksum for a packet built on IPv4.
677pub fn ipv4_checksum(packet: &TcpPacket, source: &Ipv4Addr, destination: &Ipv4Addr) -> u16 {
678    ipv4_checksum_adv(packet, &[], source, destination)
679}
680
681/// Calculate the checksum for a packet built on IPv4, Advanced version which
682/// accepts an extra slice of data that will be included in the checksum
683/// as being part of the data portion of the packet.
684///
685/// If `packet` contains an odd number of bytes the last byte will not be
686/// counted as the first byte of a word together with the first byte of
687/// `extra_data`.
688pub fn ipv4_checksum_adv(
689    packet: &TcpPacket,
690    extra_data: &[u8],
691    source: &Ipv4Addr,
692    destination: &Ipv4Addr,
693) -> u16 {
694    util::ipv4_checksum(
695        &packet.to_bytes(),
696        8,
697        extra_data,
698        source,
699        destination,
700        IpNextProtocol::Tcp,
701    )
702}
703
704/// Calculate a checksum for a packet built on IPv6.
705pub fn ipv6_checksum(packet: &TcpPacket, source: &Ipv6Addr, destination: &Ipv6Addr) -> u16 {
706    ipv6_checksum_adv(packet, &[], source, destination)
707}
708
709/// Calculate the checksum for a packet built on IPv6, Advanced version which
710/// accepts an extra slice of data that will be included in the checksum
711/// as being part of the data portion of the packet.
712///
713/// If `packet` contains an odd number of bytes the last byte will not be
714/// counted as the first byte of a word together with the first byte of
715/// `extra_data`.
716pub fn ipv6_checksum_adv(
717    packet: &TcpPacket,
718    extra_data: &[u8],
719    source: &Ipv6Addr,
720    destination: &Ipv6Addr,
721) -> u16 {
722    util::ipv6_checksum(
723        &packet.to_bytes(),
724        8,
725        extra_data,
726        source,
727        destination,
728        IpNextProtocol::Tcp,
729    )
730}
731
732#[cfg(test)]
733mod tests {
734    use super::*;
735
736    #[test]
737    fn test_basic_tcp_parse() {
738        let ref_packet = Bytes::from_static(&[
739            0xc1, 0x67, /* source */
740            0x23, 0x28, /* destination */
741            0x90, 0x37, 0xd2, 0xb8, /* seq */
742            0x94, 0x4b, 0xb2, 0x76, /* ack */
743            0x80, 0x18, 0x0f, 0xaf, /* offset+reserved, flags, win */
744            0xc0, 0x31, /* checksum */
745            0x00, 0x00, /* urg ptr */
746            0x01, 0x01, /* NOP */
747            0x08, 0x0a, 0x2c, 0x57, 0xcd, 0xa5, 0x02, 0xa0, 0x41, 0x92, /* timestamp */
748            0x74, 0x65, 0x73, 0x74, /* payload: "test" */
749        ]);
750        let packet = TcpPacket::from_bytes(ref_packet.clone()).unwrap();
751
752        assert_eq!(packet.header.source, 0xc167);
753        assert_eq!(packet.header.destination, 0x2328);
754        assert_eq!(packet.header.sequence, 0x9037d2b8);
755        assert_eq!(packet.header.acknowledgement, 0x944bb276);
756        assert_eq!(packet.header.data_offset, 8); // adjusted
757        assert_eq!(packet.header.reserved, 0);
758        assert_eq!(packet.header.flags, 0x18); // PSH + ACK
759        assert_eq!(packet.header.window, 0x0faf);
760        assert_eq!(packet.header.checksum, 0xc031);
761        assert_eq!(packet.header.urgent_ptr, 0x0000);
762        assert_eq!(packet.header.options.len(), 3);
763        assert_eq!(packet.header.options[0].kind, TcpOptionKind::NOP);
764        assert_eq!(packet.header.options[1].kind, TcpOptionKind::NOP);
765        assert_eq!(packet.header.options[2].kind, TcpOptionKind::TIMESTAMPS);
766        assert_eq!(
767            packet.header.options[2].get_timestamp(),
768            (0x2c57cda5, 0x02a04192)
769        );
770        assert_eq!(packet.payload, Bytes::from_static(b"test"));
771        assert_eq!(packet.header_len(), 32); // adjusted
772        assert_eq!(packet.to_bytes(), ref_packet);
773        assert_eq!(packet.header().len(), 32); // adjusted
774        assert_eq!(packet.payload().len(), 4);
775    }
776
777    #[test]
778    fn test_basic_tcp_create() {
779        let options = vec![
780            TcpOptionPacket::nop(),
781            TcpOptionPacket::nop(),
782            TcpOptionPacket::timestamp(0x2c57cda5, 0x02a04192),
783        ];
784
785        let packet = TcpPacket {
786            header: TcpHeader {
787                source: 0xc167,
788                destination: 0x2328,
789                sequence: 0x9037d2b8,
790                acknowledgement: 0x944bb276,
791                data_offset: 8.into(), // 8 * 4 = 32 bytes
792                reserved: 0.into(),
793                flags: 0x18, // PSH + ACK
794                window: 0x0faf,
795                checksum: 0xc031,
796                urgent_ptr: 0x0000,
797                options: options.clone(),
798            },
799            payload: Bytes::from_static(b"test"),
800        };
801
802        let bytes = packet.to_bytes();
803        let parsed = TcpPacket::from_bytes(bytes.clone()).expect("Failed to parse TCP packet");
804
805        assert_eq!(parsed, packet);
806        assert_eq!(parsed.to_bytes(), bytes);
807        assert_eq!(parsed.header.options.len(), 3);
808        assert_eq!(
809            parsed.header.options[2].get_timestamp(),
810            (0x2c57cda5, 0x02a04192)
811        );
812    }
813}