summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2023-07-28 20:11:09 +0800
committerluwenpeng <[email protected]>2023-08-01 11:44:26 +0800
commitaee47ce205a0803e3359f31124fea04f9aaf7b11 (patch)
tree58a121f5061084598ecc5cbea115fd96ee633d4f
parent7749538385eff149da2a7de73f485cdd00baa129 (diff)
[feature] Support TCP Decode
-rw-r--r--src/protocol/mod.rs3
-rw-r--r--src/protocol/tcp.rs604
2 files changed, 606 insertions, 1 deletions
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 2591e5b..cff0932 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -2,4 +2,5 @@ pub mod ethernet;
pub mod ip;
pub mod ipv4;
pub mod ipv6;
-pub mod udp; \ No newline at end of file
+pub mod udp;
+pub mod tcp; \ No newline at end of file
diff --git a/src/protocol/tcp.rs b/src/protocol/tcp.rs
new file mode 100644
index 0000000..ea424b9
--- /dev/null
+++ b/src/protocol/tcp.rs
@@ -0,0 +1,604 @@
+use nom::bits;
+use nom::error::Error;
+use nom::error::ErrorKind;
+use nom::number;
+use nom::sequence;
+use nom::Err;
+use nom::IResult;
+use nom::Needed;
+
+/******************************************************************************
+ * Struct
+ ******************************************************************************/
+
+/*
+ * TCP Header Format
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Port | Destination Port |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Acknowledgment Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data | |U|A|P|R|S|F| |
+ * | Offset| Reserved |R|C|S|S|Y|I| Window |
+ * | | |G|K|H|T|N|N| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Checksum | Urgent Pointer |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Options | Padding |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | data |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+const TCP_OPTION_EOL: u8 = 0; // End of Options list
+const TCP_OPTION_NOP: u8 = 1; // No operation
+const TCP_OPTION_MSS: u8 = 2; // Maximum segment size
+const TCP_OPTION_WSCALE: u8 = 3; // Window scale
+const TCP_OPTION_SACK_PERMITTED: u8 = 4; // Selective acknowledgements permitted
+const TCP_OPTION_SACK: u8 = 5; // Selective acknowledgment
+const TCP_OPTION_TIMESTAMPS: u8 = 8; // Timestamps
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum TcpOption {
+ EOL,
+ NOP,
+ MSS {
+ length: u8,
+ mss: u16,
+ },
+ WSCALE {
+ length: u8,
+ shift_count: u8,
+ },
+ SACKPERMITTED,
+ SACK {
+ length: u8,
+ left_edge: u32,
+ right_edge: u32,
+ },
+ TIMESTAMPS {
+ length: u8,
+ ts_value: u32,
+ ts_reply: u32,
+ },
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Default)]
+pub struct TcpHeader {
+ pub source_port: u16,
+ pub dest_port: u16,
+
+ pub seq_num: u32,
+ pub ack_num: u32,
+
+ pub data_offset: u8,
+ pub reserved: u8,
+
+ pub flag_urg: bool,
+ pub flag_ack: bool,
+ pub flag_psh: bool,
+ pub flag_rst: bool,
+ pub flag_syn: bool,
+ pub flag_fin: bool,
+
+ pub window: u16,
+ pub checksum: u16,
+ pub urgent_ptr: u16,
+ pub options: Option<Vec<TcpOption>>,
+}
+
+/******************************************************************************
+ * API
+ ******************************************************************************/
+
+fn offset_res_flags_decode(input: &[u8]) -> IResult<&[u8], (u8, u8, u8)> {
+ bits::bits::<_, _, Error<_>, _, _>(sequence::tuple((
+ bits::streaming::take(4u8),
+ bits::streaming::take(6u8),
+ bits::streaming::take(6u8),
+ )))(input)
+}
+
+impl TcpOption {
+ fn mss_decode(input: &[u8]) -> IResult<&[u8], TcpOption> {
+ let (input, length) = number::streaming::be_u8(input)?;
+ let (input, mss) = number::streaming::be_u16(input)?;
+ Ok((input, TcpOption::MSS { length, mss }))
+ }
+
+ fn wscale_decode(input: &[u8]) -> IResult<&[u8], TcpOption> {
+ let (input, length) = number::streaming::be_u8(input)?;
+ let (input, shift_count) = number::streaming::be_u8(input)?;
+ Ok((
+ input,
+ TcpOption::WSCALE {
+ length,
+ shift_count,
+ },
+ ))
+ }
+
+ fn sack_permitted_decode(input: &[u8]) -> IResult<&[u8], TcpOption> {
+ let (input, _length) = number::streaming::be_u8(input)?;
+ Ok((input, TcpOption::SACKPERMITTED))
+ }
+
+ fn sack_decode(input: &[u8]) -> IResult<&[u8], TcpOption> {
+ let (input, length) = number::streaming::be_u8(input)?;
+ let (input, left_edge) = number::streaming::be_u32(input)?;
+ let (input, right_edge) = number::streaming::be_u32(input)?;
+ Ok((
+ input,
+ TcpOption::SACK {
+ length,
+ left_edge,
+ right_edge,
+ },
+ ))
+ }
+
+ fn timestamp_decode(input: &[u8]) -> IResult<&[u8], TcpOption> {
+ let (input, length) = number::streaming::be_u8(input)?;
+ let (input, ts_value) = number::streaming::be_u32(input)?;
+ let (input, ts_reply) = number::streaming::be_u32(input)?;
+ Ok((
+ input,
+ TcpOption::TIMESTAMPS {
+ length,
+ ts_value,
+ ts_reply,
+ },
+ ))
+ }
+
+ fn decode(input: &[u8]) -> IResult<&[u8], TcpOption> {
+ match number::streaming::be_u8(input)? {
+ (input, TCP_OPTION_EOL) => Ok((input, TcpOption::EOL)),
+ (input, TCP_OPTION_NOP) => Ok((input, TcpOption::NOP)),
+ (input, TCP_OPTION_MSS) => TcpOption::mss_decode(input),
+ (input, TCP_OPTION_WSCALE) => TcpOption::wscale_decode(input),
+ (input, TCP_OPTION_SACK_PERMITTED) => TcpOption::sack_permitted_decode(input),
+ (input, TCP_OPTION_SACK) => TcpOption::sack_decode(input),
+ (input, TCP_OPTION_TIMESTAMPS) => TcpOption::timestamp_decode(input),
+ (input, _other) => Err(Err::Failure(Error::new(input, ErrorKind::Switch))),
+ }
+ }
+}
+
+impl TcpHeader {
+ fn fixed_header_decode(input: &[u8]) -> IResult<&[u8], TcpHeader> {
+ let (input, source_port) = number::streaming::be_u16(input)?;
+ let (input, dest_port) = number::streaming::be_u16(input)?;
+ let (input, seq_num) = number::streaming::be_u32(input)?;
+ let (input, ack_num) = number::streaming::be_u32(input)?;
+ let (input, dataof_res_flags) = offset_res_flags_decode(input)?;
+ let (input, window) = number::streaming::be_u16(input)?;
+ let (input, checksum) = number::streaming::be_u16(input)?;
+ let (input, urgent_ptr) = number::streaming::be_u16(input)?;
+
+ Ok((
+ input,
+ TcpHeader {
+ source_port,
+ dest_port,
+ seq_num,
+ ack_num,
+ data_offset: dataof_res_flags.0 * 4, // dataof_res_flags.0 * 32 / 8
+ reserved: dataof_res_flags.1,
+ flag_urg: dataof_res_flags.2 & 0b10_0000 == 0b10_0000,
+ flag_ack: dataof_res_flags.2 & 0b01_0000 == 0b01_0000,
+ flag_psh: dataof_res_flags.2 & 0b00_1000 == 0b00_1000,
+ flag_rst: dataof_res_flags.2 & 0b00_0100 == 0b00_0100,
+ flag_syn: dataof_res_flags.2 & 0b00_0010 == 0b00_0010,
+ flag_fin: dataof_res_flags.2 & 0b00_0001 == 0b00_0001,
+ window,
+ checksum,
+ urgent_ptr,
+ options: None,
+ },
+ ))
+ }
+
+ fn options_decode(input: &[u8]) -> IResult<&[u8], Vec<TcpOption>> {
+ let mut left = input;
+ let mut options: Vec<TcpOption> = vec![];
+ loop {
+ match TcpOption::decode(left) {
+ Ok((l, opt)) => {
+ left = l;
+ options.push(opt);
+
+ if left.len() <= 0 {
+ break;
+ }
+ if let TcpOption::EOL = opt {
+ break;
+ }
+ }
+ Err(e) => return Err(e),
+ }
+ }
+
+ Ok((left, options))
+ }
+
+ pub fn decode(input: &[u8]) -> IResult<&[u8], TcpHeader> {
+ match TcpHeader::fixed_header_decode(input) {
+ Ok((left, mut header)) => {
+ if header.data_offset > 20 {
+ let options_length = (header.data_offset - 20) as usize;
+ if options_length <= left.len() {
+ if let Ok((__left, options)) =
+ TcpHeader::options_decode(&left[0..options_length])
+ {
+ header.options = Some(options);
+ Ok((&left[options_length..], header))
+ } else {
+ println!("tcp header options parser error, skip tcp options");
+ Ok((&left[options_length..], header))
+ }
+ } else {
+ println!("tcp header options data not enough");
+ Err(Err::Incomplete(Needed::new(options_length - left.len())))
+ }
+ } else {
+ Ok((left, header))
+ }
+ }
+ err => err,
+ }
+ }
+}
+
+/******************************************************************************
+ * TEST
+ ******************************************************************************/
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::protocol::tcp::TcpOption::MSS;
+ use crate::protocol::tcp::TcpOption::NOP;
+ use crate::protocol::tcp::TcpOption::SACK;
+ use crate::protocol::tcp::TcpOption::SACKPERMITTED;
+ use crate::protocol::tcp::TcpOption::TIMESTAMPS;
+ use crate::protocol::tcp::TcpOption::WSCALE;
+
+ const LAST_SLICE: &'static [u8] = &[0xff];
+
+ #[test]
+ fn tcp_header_decode1() {
+ /*
+ * Transmission Control Protocol, Src Port: 50081, Dst Port: 443, Seq: 1, Ack: 1, Len: 1348
+ * Source Port: 50081
+ * Destination Port: 443
+ * [Stream index: 0]
+ * [Conversation completeness: Incomplete (8)]
+ * [TCP Segment Len: 1348]
+ * Sequence Number: 1 (relative sequence number)
+ * Sequence Number (raw): 1522577104
+ * [Next Sequence Number: 1349 (relative sequence number)]
+ * Acknowledgment Number: 1 (relative ack number)
+ * Acknowledgment number (raw): 3419365570
+ * 1000 .... = Header Length: 32 bytes (8)
+ * Flags: 0x010 (ACK)
+ * 000. .... .... = Reserved: Not set
+ * ...0 .... .... = Nonce: Not set
+ * .... 0... .... = Congestion Window Reduced (CWR): Not set
+ * .... .0.. .... = ECN-Echo: Not set
+ * .... ..0. .... = Urgent: Not set
+ * .... ...1 .... = Acknowledgment: Set
+ * .... .... 0... = Push: Not set
+ * .... .... .0.. = Reset: Not set
+ * .... .... ..0. = Syn: Not set
+ * .... .... ...0 = Fin: Not set
+ * [TCP Flags: ·······A····]
+ * Window: 2038
+ * [Calculated window size: 2038]
+ * [Window size scaling factor: -1 (unknown)]
+ * Checksum: 0xd3c2 [correct]
+ * [Checksum Status: Good]
+ * [Calculated Checksum: 0xd3c2]
+ * Urgent Pointer: 0
+ * Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps
+ * TCP Option - No-Operation (NOP)
+ * Kind: No-Operation (1)
+ * TCP Option - No-Operation (NOP)
+ * Kind: No-Operation (1)
+ * TCP Option - Timestamps: TSval 2232684208, TSecr 3427137631
+ * Kind: Time Stamp Option (8)
+ * Length: 10
+ * Timestamp value: 2232684208
+ * Timestamp echo reply: 3427137631
+ * [Timestamps]
+ * [Time since first frame in this TCP stream: 0.000000000 seconds]
+ * [Time since previous frame in this TCP stream: 0.000000000 seconds]
+ */
+
+ let bytes = [
+ 0xc3, 0xa1, /* Source Port */
+ 0x01, 0xbb, /* Destination Port */
+ 0x5a, 0xc0, 0xae, 0xd0, /* Sequence Number */
+ 0xcb, 0xcf, 0x60, 0xc2, /* Acknowledgment Number */
+ 0x80, 0x10, /* DataOffset and Reserved and Flags */
+ 0x07, 0xf6, /* Window */
+ 0xd3, 0xc2, /* Checksum */
+ 0x00, 0x00, /* Urgent Pointer */
+ 0x01, /* TCP Option - No-Operation */
+ 0x01, /* TCP Option - No-Operation */
+ 0x08, 0x0a, 0x85, 0x14, 0x0e, 0xb0, 0xcc, 0x45, 0xf8,
+ 0x5f, /* TCP Option - Timestamps */
+ 0xff, /* Payload */
+ ];
+
+ let expect_options: Vec<TcpOption> = vec![
+ NOP,
+ NOP,
+ TIMESTAMPS {
+ length: 10,
+ ts_value: 2232684208,
+ ts_reply: 3427137631,
+ },
+ ];
+
+ let expectation = TcpHeader {
+ source_port: 50081,
+ dest_port: 443,
+ seq_num: 1522577104,
+ ack_num: 3419365570,
+ data_offset: 32,
+ reserved: 0,
+ flag_urg: false,
+ flag_ack: true,
+ flag_psh: false,
+ flag_rst: false,
+ flag_syn: false,
+ flag_fin: false,
+ window: 2038,
+ checksum: 0xd3c2,
+ urgent_ptr: 0,
+ options: Some(expect_options),
+ };
+
+ assert_eq!(TcpHeader::decode(&bytes), Ok((LAST_SLICE, expectation)));
+ }
+
+ #[test]
+ fn tcp_header_decode2() {
+ /*
+ * Transmission Control Protocol, Src Port: 58816, Dst Port: 80, Seq: 0, Len: 0
+ * Source Port: 58816
+ * Destination Port: 80
+ * [Stream index: 0]
+ * [Conversation completeness: Incomplete, DATA (15)]
+ * [TCP Segment Len: 0]
+ * Sequence Number: 0 (relative sequence number)
+ * Sequence Number (raw): 3851697578
+ * [Next Sequence Number: 1 (relative sequence number)]
+ * Acknowledgment Number: 0
+ * Acknowledgment number (raw): 0
+ * 1010 .... = Header Length: 40 bytes (10)
+ * Flags: 0x002 (SYN)
+ * 000. .... .... = Reserved: Not set
+ * ...0 .... .... = Nonce: Not set
+ * .... 0... .... = Congestion Window Reduced (CWR): Not set
+ * .... .0.. .... = ECN-Echo: Not set
+ * .... ..0. .... = Urgent: Not set
+ * .... ...0 .... = Acknowledgment: Not set
+ * .... .... 0... = Push: Not set
+ * .... .... .0.. = Reset: Not set
+ * .... .... ..1. = Syn: Set
+ * .... .... ...0 = Fin: Not set
+ * [TCP Flags: ··········S·]
+ * Window: 5840
+ * [Calculated window size: 5840]
+ * Checksum: 0x9de2 [correct]
+ * [Checksum Status: Good]
+ * [Calculated Checksum: 0x9de2]
+ * Urgent Pointer: 0
+ * Options: (20 bytes), Maximum segment size, SACK permitted, Timestamps, No-Operation (NOP), Window scale
+ * TCP Option - Maximum segment size: 1460 bytes
+ * Kind: Maximum Segment Size (2)
+ * Length: 4
+ * MSS Value: 1460
+ * TCP Option - SACK permitted
+ * Kind: SACK Permitted (4)
+ * Length: 2
+ * TCP Option - Timestamps: TSval 1545573, TSecr 0
+ * Kind: Time Stamp Option (8)
+ * Length: 10
+ * Timestamp value: 1545573
+ * Timestamp echo reply: 0
+ * TCP Option - No-Operation (NOP)
+ * Kind: No-Operation (1)
+ * TCP Option - Window scale: 7 (multiply by 128)
+ * Kind: Window Scale (3)
+ * Length: 3
+ * Shift count: 7
+ * [Multiplier: 128]
+ * [Timestamps]
+ * [Time since first frame in this TCP stream: 0.000000000 seconds]
+ * [Time since previous frame in this TCP stream: 0.000000000 seconds]
+ */
+
+ let bytes = [
+ 0xe5, 0xc0, /* Source Port */
+ 0x00, 0x50, /* Destination Port */
+ 0xe5, 0x94, 0x3d, 0xaa, /* Sequence Number */
+ 0x00, 0x00, 0x00, 0x00, /* Acknowledgment Number */
+ 0xa0, 0x02, /* DataOffset and Reserved and Flags */
+ 0x16, 0xd0, /* Window */
+ 0x9d, 0xe2, /* Checksum */
+ 0x00, 0x00, /* Urgent Pointer */
+ 0x02, 0x04, 0x05, 0xb4, /* TCP Option - Maximum segment size */
+ 0x04, 0x02, /* TCP Option - SACK permitted */
+ 0x08, 0x0a, 0x00, 0x17, 0x95, 0x65, 0x00, 0x00, 0x00,
+ 0x00, /* TCP Option - Timestamps */
+ 0x01, /* TCP Option - No-Operation */
+ 0x03, 0x03, 0x07, /* TCP Option - Window scale */
+ 0xff, /* Payload */
+ ];
+
+ let expect_options: Vec<TcpOption> = vec![
+ MSS {
+ length: 4,
+ mss: 1460,
+ },
+ SACKPERMITTED,
+ TIMESTAMPS {
+ length: 10,
+ ts_value: 1545573,
+ ts_reply: 0,
+ },
+ NOP,
+ WSCALE {
+ length: 3,
+ shift_count: 7,
+ },
+ ];
+
+ let expectation = TcpHeader {
+ source_port: 58816,
+ dest_port: 80,
+ seq_num: 3851697578,
+ ack_num: 0,
+ data_offset: 40,
+ reserved: 0,
+ flag_urg: false,
+ flag_ack: false,
+ flag_psh: false,
+ flag_rst: false,
+ flag_syn: true,
+ flag_fin: false,
+ window: 5840,
+ checksum: 0x9de2,
+ urgent_ptr: 0,
+ options: Some(expect_options),
+ };
+
+ assert_eq!(TcpHeader::decode(&bytes), Ok((LAST_SLICE, expectation)));
+ }
+
+ #[test]
+ fn tcp_header_decode3() {
+ /*
+ * Transmission Control Protocol, Src Port: 58816, Dst Port: 80, Seq: 461, Ack: 17377, Len: 0
+ * Source Port: 58816
+ * Destination Port: 80
+ * [Stream index: 0]
+ * [Conversation completeness: Incomplete, DATA (15)]
+ * [TCP Segment Len: 0]
+ * Sequence Number: 461 (relative sequence number)
+ * Sequence Number (raw): 3851698039
+ * [Next Sequence Number: 461 (relative sequence number)]
+ * Acknowledgment Number: 17377 (relative ack number)
+ * Acknowledgment number (raw): 2747581568
+ * 1011 .... = Header Length: 44 bytes (11)
+ * Flags: 0x010 (ACK)
+ * 000. .... .... = Reserved: Not set
+ * ...0 .... .... = Nonce: Not set
+ * .... 0... .... = Congestion Window Reduced (CWR): Not set
+ * .... .0.. .... = ECN-Echo: Not set
+ * .... ..0. .... = Urgent: Not set
+ * .... ...1 .... = Acknowledgment: Set
+ * .... .... 0... = Push: Not set
+ * .... .... .0.. = Reset: Not set
+ * .... .... ..0. = Syn: Not set
+ * .... .... ...0 = Fin: Not set
+ * [TCP Flags: ·······A····]
+ * Window: 318
+ * [Calculated window size: 40704]
+ * [Window size scaling factor: 128]
+ * Checksum: 0x34b6 [correct]
+ * [Checksum Status: Good]
+ * [Calculated Checksum: 0x34b6]
+ * Urgent Pointer: 0
+ * Options: (24 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps, No-Operation (NOP), No-Operation (NOP), SACK
+ * TCP Option - No-Operation (NOP)
+ * Kind: No-Operation (1)
+ * TCP Option - No-Operation (NOP)
+ * Kind: No-Operation (1)
+ * TCP Option - Timestamps: TSval 1545583, TSecr 2375917095
+ * Kind: Time Stamp Option (8)
+ * Length: 10
+ * Timestamp value: 1545583
+ * Timestamp echo reply: 2375917095
+ * TCP Option - No-Operation (NOP)
+ * Kind: No-Operation (1)
+ * TCP Option - No-Operation (NOP)
+ * Kind: No-Operation (1)
+ * TCP Option - SACK 18825-20273
+ * Kind: SACK (5)
+ * Length: 10
+ * left edge = 18825 (relative)
+ * right edge = 20273 (relative)
+ * [TCP SACK Count: 1]
+ * [Timestamps]
+ * [Time since first frame in this TCP stream: 0.100262000 seconds]
+ * [Time since previous frame in this TCP stream: 0.000007000 seconds]
+ */
+
+ let bytes = [
+ 0xe5, 0xc0, /* Source Port */
+ 0x00, 0x50, /* Destination Port */
+ 0xe5, 0x94, 0x3f, 0x77, /* Sequence Number */
+ 0xa3, 0xc4, 0xc4, 0x80, /* Acknowledgment Number */
+ 0xb0, 0x10, /* DataOffset and Reserved and Flags */
+ 0x01, 0x3e, /* Window */
+ 0x34, 0xb6, /* Checksum */
+ 0x00, 0x00, /* Urgent Pointer */
+ 0x01, /* TCP Option - No-Operation */
+ 0x01, /* TCP Option - No-Operation */
+ 0x08, 0x0a, 0x00, 0x17, 0x95, 0x6f, 0x8d, 0x9d, 0x9e,
+ 0x27, /* TCP Option - Timestamps */
+ 0x01, /* TCP Option - No-Operation */
+ 0x01, /* TCP Option - No-Operation */
+ 0x05, 0x0a, 0xa3, 0xc4, 0xca, 0x28, 0xa3, 0xc4, 0xcf,
+ 0xd0, /* TCP Option - SACK */
+ 0xff, /* Payload */
+ ];
+
+ let opts: Vec<TcpOption> = vec![
+ NOP,
+ NOP,
+ TIMESTAMPS {
+ length: 10,
+ ts_value: 1545583,
+ ts_reply: 2375917095,
+ },
+ NOP,
+ NOP,
+ SACK {
+ length: 10,
+ left_edge: 2747583016,
+ right_edge: 2747584464,
+ },
+ ];
+
+ let expectation = TcpHeader {
+ source_port: 58816,
+ dest_port: 80,
+ seq_num: 3851698039,
+ ack_num: 2747581568,
+ data_offset: 44,
+ reserved: 0,
+ flag_urg: false,
+ flag_ack: true,
+ flag_psh: false,
+ flag_rst: false,
+ flag_syn: false,
+ flag_fin: false,
+ window: 318,
+ checksum: 0x34b6,
+ urgent_ptr: 0,
+ options: Some(opts),
+ };
+
+ assert_eq!(TcpHeader::decode(&bytes), Ok((LAST_SLICE, expectation)));
+ }
+}