pnet_packet/
tcp.rs

1// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! A TCP packet abstraction.
10
11use crate::Packet;
12use crate::PrimitiveValues;
13use crate::ip::IpNextHeaderProtocols;
14
15use alloc::{vec, vec::Vec};
16
17use pnet_macros::packet;
18use pnet_macros_support::types::*;
19
20use pnet_base::core_net::Ipv4Addr;
21use pnet_base::core_net::Ipv6Addr;
22use crate::util::{self, Octets};
23
24/// The TCP flags.
25#[allow(non_snake_case)]
26#[allow(non_upper_case_globals)]
27pub mod TcpFlags {
28    /// CWR – Congestion Window Reduced (CWR) flag is set by the sending
29    /// host to indicate that it received a TCP segment with the ECE flag set
30    /// and had responded in congestion control mechanism (added to header by RFC 3168).
31    pub const CWR: u8 = 0b10000000;
32    /// ECE – ECN-Echo has a dual role, depending on the value of the
33    /// SYN flag. It indicates:
34    /// If the SYN flag is set (1), that the TCP peer is ECN capable.
35    /// If the SYN flag is clear (0), that a packet with Congestion Experienced
36    /// flag set (ECN=11) in IP header received during normal transmission
37    /// (added to header by RFC 3168).
38    pub const ECE: u8 = 0b01000000;
39    /// URG – indicates that the Urgent pointer field is significant.
40    pub const URG: u8 = 0b00100000;
41    /// ACK – indicates that the Acknowledgment field is significant.
42    /// All packets after the initial SYN packet sent by the client should have this flag set.
43    pub const ACK: u8 = 0b00010000;
44    /// PSH – Push function. Asks to push the buffered data to the receiving application.
45    pub const PSH: u8 = 0b00001000;
46    /// RST – Reset the connection.
47    pub const RST: u8 = 0b00000100;
48    /// SYN – Synchronize sequence numbers. Only the first packet sent from each end
49    /// should have this flag set.
50    pub const SYN: u8 = 0b00000010;
51    /// FIN – No more data from sender.
52    pub const FIN: u8 = 0b00000001;
53}
54
55/// Represents a TCP packet.
56#[packet]
57pub struct Tcp {
58    pub source: u16be,
59    pub destination: u16be,
60    pub sequence: u32be,
61    pub acknowledgement: u32be,
62    pub data_offset: u4,
63    pub reserved: u4,
64    pub flags: u8,
65    pub window: u16be,
66    pub checksum: u16be,
67    pub urgent_ptr: u16be,
68    #[length_fn = "tcp_options_length"]
69    pub options: Vec<TcpOption>,
70    #[payload]
71    pub payload: Vec<u8>,
72}
73
74/// Represents a TCP option.
75#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub struct TcpOptionNumber(pub u8);
77
78/// The TCP header options.
79#[allow(non_snake_case)]
80#[allow(non_upper_case_globals)]
81pub mod TcpOptionNumbers {
82    use super::TcpOptionNumber;
83
84    /// End of Options list.
85    pub const EOL: TcpOptionNumber = TcpOptionNumber(0);
86
87    /// No operation.
88    pub const NOP: TcpOptionNumber = TcpOptionNumber(1);
89
90    /// Maximum segment size.
91    pub const MSS: TcpOptionNumber = TcpOptionNumber(2);
92
93    /// Window scale.
94    pub const WSCALE: TcpOptionNumber = TcpOptionNumber(3);
95
96    /// Selective acknowledgements permitted.
97    pub const SACK_PERMITTED: TcpOptionNumber = TcpOptionNumber(4);
98
99    /// Selective acknowledgment.
100    pub const SACK: TcpOptionNumber = TcpOptionNumber(5);
101
102    /// Timestamps.
103    pub const TIMESTAMPS: TcpOptionNumber = TcpOptionNumber(8);
104}
105
106/// A TCP option.
107#[packet]
108pub struct TcpOption {
109    #[construct_with(u8)]
110    pub number: TcpOptionNumber,
111    #[length_fn = "tcp_option_length"]
112    // The length field is an optional field, using a Vec is a way to implement it.
113    pub length: Vec<u8>,
114    #[length_fn = "tcp_option_payload_length"]
115    #[payload]
116    pub data: Vec<u8>,
117}
118
119impl TcpOption {
120    /// NOP: This may be used to align option fields on 32-bit boundaries for better performance.
121    pub fn nop() -> Self {
122        TcpOption {
123            number: TcpOptionNumbers::NOP,
124            length: vec![],
125            data: vec![],
126        }
127    }
128
129    /// Timestamp: TCP timestamps, defined in RFC 1323, can help TCP determine in which order
130    /// packets were sent. TCP timestamps are not normally aligned to the system clock and
131    /// start at some random value.
132    pub fn timestamp(my: u32, their: u32) -> Self {
133        let mut data = vec![];
134        data.extend_from_slice(&my.octets()[..]);
135        data.extend_from_slice(&their.octets()[..]);
136
137        TcpOption {
138            number: TcpOptionNumbers::TIMESTAMPS,
139            length: vec![10],
140            data: data,
141        }
142    }
143
144    /// MSS: The maximum segment size (MSS) is the largest amount of data, specified in bytes,
145    /// that TCP is willing to receive in a single segment.
146    pub fn mss(val: u16) -> Self {
147        let mut data = vec![];
148        data.extend_from_slice(&val.octets()[..]);
149
150        TcpOption {
151            number: TcpOptionNumbers::MSS,
152            length: vec![4],
153            data: data,
154        }
155    }
156
157    /// Window scale: The TCP window scale option, as defined in RFC 1323, is an option used to
158    /// increase the maximum window size from 65,535 bytes to 1 gigabyte.
159    pub fn wscale(val: u8) -> Self {
160        TcpOption {
161            number: TcpOptionNumbers::WSCALE,
162            length: vec![3],
163            data: vec![val],
164        }
165    }
166
167    /// Selective acknowledgment (SACK) option, defined in RFC 2018 allows the receiver to acknowledge
168    /// discontinuous blocks of packets which were received correctly. This options enables use of
169    /// SACK during negotiation.
170    pub fn sack_perm() -> Self {
171        TcpOption {
172            number: TcpOptionNumbers::SACK_PERMITTED,
173            length: vec![2],
174            data: vec![],
175        }
176    }
177
178    /// Selective acknowledgment (SACK) option, defined in RFC 2018 allows the receiver to acknowledge
179    /// discontinuous blocks of packets which were received correctly. The acknowledgement can specify
180    /// a number of SACK blocks, where each SACK block is conveyed by the starting and ending sequence
181    /// numbers of a contiguous range that the receiver correctly received.
182    pub fn selective_ack(acks: &[u32]) -> Self {
183        let mut data = vec![];
184        for ack in acks {
185            data.extend_from_slice(&ack.octets()[..]);
186        }
187        TcpOption {
188            number: TcpOptionNumbers::SACK,
189            length: vec![1 /* number */ + 1 /* length */ + data.len() as u8],
190            data: data,
191        }
192    }
193}
194
195/// This function gets the 'length' of the length field of the IPv4Option packet
196/// Few options (EOL, NOP) are 1 bytes long, and then have a length field equal
197/// to 0.
198#[inline]
199fn tcp_option_length(option: &TcpOptionPacket) -> usize {
200    match option.get_number() {
201        TcpOptionNumbers::EOL => 0,
202        TcpOptionNumbers::NOP => 0,
203        _ => 1,
204    }
205}
206
207fn tcp_option_payload_length(ipv4_option: &TcpOptionPacket) -> usize {
208    match ipv4_option.get_length_raw().first() {
209        Some(len) if *len >= 2 => *len as usize - 2,
210        _ => 0,
211    }
212}
213
214impl TcpOptionNumber {
215    /// Create a new `TcpOptionNumber` instance.
216    pub fn new(value: u8) -> TcpOptionNumber {
217        TcpOptionNumber(value)
218    }
219}
220
221impl PrimitiveValues for TcpOptionNumber {
222    type T = (u8,);
223    fn to_primitive_values(&self) -> (u8,) {
224        (self.0,)
225    }
226}
227
228#[inline]
229fn tcp_options_length(tcp: &TcpPacket) -> usize {
230    let data_offset = tcp.get_data_offset();
231
232    if data_offset > 5 {
233        data_offset as usize * 4 - 20
234    } else {
235        0
236    }
237}
238
239/// Calculate a checksum for a packet built on IPv4.
240pub fn ipv4_checksum(packet: &TcpPacket, source: &Ipv4Addr, destination: &Ipv4Addr) -> u16 {
241    ipv4_checksum_adv(packet, &[], source, destination)
242}
243
244/// Calculate the checksum for a packet built on IPv4, Advanced version which
245/// accepts an extra slice of data that will be included in the checksum
246/// as being part of the data portion of the packet.
247///
248/// If `packet` contains an odd number of bytes the last byte will not be
249/// counted as the first byte of a word together with the first byte of
250/// `extra_data`.
251pub fn ipv4_checksum_adv(packet: &TcpPacket,
252                         extra_data: &[u8],
253                         source: &Ipv4Addr,
254                         destination: &Ipv4Addr)
255    -> u16 {
256    util::ipv4_checksum(packet.packet(),
257                        8,
258                        extra_data,
259                        source,
260                        destination,
261                        IpNextHeaderProtocols::Tcp)
262}
263
264/// Calculate a checksum for a packet built on IPv6.
265pub fn ipv6_checksum(packet: &TcpPacket, source: &Ipv6Addr, destination: &Ipv6Addr) -> u16 {
266    ipv6_checksum_adv(packet, &[], source, destination)
267}
268
269/// Calculate the checksum for a packet built on IPv6, Advanced version which
270/// accepts an extra slice of data that will be included in the checksum
271/// as being part of the data portion of the packet.
272///
273/// If `packet` contains an odd number of bytes the last byte will not be
274/// counted as the first byte of a word together with the first byte of
275/// `extra_data`.
276pub fn ipv6_checksum_adv(packet: &TcpPacket,
277                         extra_data: &[u8],
278                         source: &Ipv6Addr,
279                         destination: &Ipv6Addr)
280    -> u16 {
281    util::ipv6_checksum(packet.packet(),
282                        8,
283                        extra_data,
284                        source,
285                        destination,
286                        IpNextHeaderProtocols::Tcp)
287}
288
289#[test]
290fn tcp_header_ipv4_test() {
291    use crate::ip::IpNextHeaderProtocols;
292    use crate::ipv4::MutableIpv4Packet;
293
294    const IPV4_HEADER_LEN: usize = 20;
295    const TCP_HEADER_LEN: usize = 32;
296    const TEST_DATA_LEN: usize = 4;
297
298    let mut packet = [0u8; IPV4_HEADER_LEN + TCP_HEADER_LEN + TEST_DATA_LEN];
299    let ipv4_source = Ipv4Addr::new(192, 168, 2, 1);
300    let ipv4_destination = Ipv4Addr::new(192, 168, 111, 51);
301    {
302        let mut ip_header = MutableIpv4Packet::new(&mut packet[..]).unwrap();
303        ip_header.set_next_level_protocol(IpNextHeaderProtocols::Tcp);
304        ip_header.set_source(ipv4_source);
305        ip_header.set_destination(ipv4_destination);
306    }
307
308    // Set data
309    packet[IPV4_HEADER_LEN + TCP_HEADER_LEN] = 't' as u8;
310    packet[IPV4_HEADER_LEN + TCP_HEADER_LEN + 1] = 'e' as u8;
311    packet[IPV4_HEADER_LEN + TCP_HEADER_LEN + 2] = 's' as u8;
312    packet[IPV4_HEADER_LEN + TCP_HEADER_LEN + 3] = 't' as u8;
313
314    {
315        let mut tcp_header = MutableTcpPacket::new(&mut packet[IPV4_HEADER_LEN..]).unwrap();
316        tcp_header.set_source(49511);
317        assert_eq!(tcp_header.get_source(), 49511);
318
319        tcp_header.set_destination(9000);
320        assert_eq!(tcp_header.get_destination(), 9000);
321
322        tcp_header.set_sequence(0x9037d2b8);
323        assert_eq!(tcp_header.get_sequence(), 0x9037d2b8);
324
325        tcp_header.set_acknowledgement(0x944bb276);
326        assert_eq!(tcp_header.get_acknowledgement(), 0x944bb276);
327
328        tcp_header.set_flags(TcpFlags::PSH | TcpFlags::ACK);
329        assert_eq!(tcp_header.get_flags(), TcpFlags::PSH | TcpFlags::ACK);
330
331        tcp_header.set_window(4015);
332        assert_eq!(tcp_header.get_window(), 4015);
333
334        tcp_header.set_data_offset(8);
335        assert_eq!(tcp_header.get_data_offset(), 8);
336
337        let ts = TcpOption::timestamp(743951781, 44056978);
338        tcp_header.set_options(&vec![TcpOption::nop(), TcpOption::nop(), ts]);
339
340        let checksum = ipv4_checksum(&tcp_header.to_immutable(), &ipv4_source, &ipv4_destination);
341        tcp_header.set_checksum(checksum);
342        assert_eq!(tcp_header.get_checksum(), 0xc031);
343    }
344    let ref_packet = [0xc1, 0x67, /* source */
345                      0x23, 0x28, /* destination */
346                      0x90, 0x37, 0xd2, 0xb8, /* seq */
347                      0x94, 0x4b, 0xb2, 0x76, /* ack */
348                      0x80, 0x18, 0x0f, 0xaf, /* length, flags, win */
349                      0xc0, 0x31, /* checksum */
350                      0x00, 0x00,  /* urg ptr */
351                      0x01, 0x01, /* options: nop */
352                      0x08, 0x0a, 0x2c, 0x57,
353                      0xcd, 0xa5, 0x02, 0xa0,
354                      0x41, 0x92, /* timestamp */
355                      0x74, 0x65, 0x73, 0x74 /* "test" */
356                      ];
357    assert_eq!(&ref_packet[..], &packet[20..]);
358}
359
360#[test]
361fn tcp_test_options_invalid_offset() {
362    let mut buf = [0; 20]; // no space for options
363    {
364        if let Some(mut tcp) = MutableTcpPacket::new(&mut buf[..]) {
365            tcp.set_data_offset(10); // set invalid offset
366        }
367    }
368
369    if let Some(tcp) = TcpPacket::new(&buf[..]) {
370        let _options = tcp.get_options_iter(); // shouldn't crash here
371    }
372}
373
374#[test]
375fn tcp_test_options_vec_invalid_offset() {
376    let mut buf = [0; 20]; // no space for options
377    {
378        if let Some(mut tcp) = MutableTcpPacket::new(&mut buf[..]) {
379            tcp.set_data_offset(10); // set invalid offset
380        }
381    }
382
383    if let Some(tcp) = TcpPacket::new(&buf[..]) {
384        let _options = tcp.get_options(); // shouldn't crash here
385    }
386}
387
388#[test]
389fn tcp_test_options_slice_invalid_offset() {
390    let mut buf = [0; 20]; // no space for options
391    {
392        if let Some(mut tcp) = MutableTcpPacket::new(&mut buf[..]) {
393            tcp.set_data_offset(10); // set invalid offset
394        }
395    }
396
397    if let Some(tcp) = TcpPacket::new(&buf[..]) {
398        let _options = tcp.get_options_raw(); // shouldn't crash here
399    }
400}
401
402#[test]
403fn tcp_test_option_invalid_len() {
404    use std::println;
405    let mut buf = [0; 24];
406    {
407        if let Some(mut tcp) = MutableTcpPacket::new(&mut buf[..]) {
408            tcp.set_data_offset(6);
409        }
410        buf[20] = 2; // option type
411        buf[21] = 8; // option len, not enough space for it
412    }
413
414    if let Some(tcp) = TcpPacket::new(&buf[..]) {
415        let options = tcp.get_options_iter();
416        for opt in options {
417            println!("{:?}", opt);
418        }
419    }
420}
421
422#[test]
423fn tcp_test_payload_slice_invalid_offset() {
424    let mut buf = [0; 20];
425    {
426        if let Some(mut tcp) = MutableTcpPacket::new(&mut buf[..]) {
427            tcp.set_data_offset(10); // set invalid offset
428        }
429    }
430
431    if let Some(tcp) = TcpPacket::new(&buf[..]) {
432        assert_eq!(tcp.payload().len(), 0);
433    }
434}