1
+ /* SPDX-License-Identifier: GPL-3.0-or-later */
2
+ /* Copyright © 2016-2024 Byteduck */
3
+
4
+ #pragma once
5
+
6
+ #include "endian.h"
7
+
8
+ #define TCP_FIN 0x01
9
+ #define TCP_SYN 0x02
10
+ #define TCP_RST 0x04
11
+ #define TCP_PSH 0x08
12
+ #define TCP_ACK 0x10
13
+ #define TCP_URG 0x20
14
+
15
+ enum TCPOption {
16
+ End = 0 ,
17
+ Nop = 1 ,
18
+ MSS = 2 ,
19
+ WindowScale = 3 ,
20
+ SACKPermit = 4 ,
21
+ SACK = 5 ,
22
+ Timestamp = 6
23
+ };
24
+
25
+ struct TCPSegment {
26
+ BigEndian < uint16_t > source_port ;
27
+ BigEndian < uint16_t > dest_port ;
28
+ BigEndian < uint32_t > sequence ;
29
+ BigEndian < uint32_t > ack ;
30
+ BigEndian < uint16_t > flags_and_offset ;
31
+ BigEndian < uint16_t > window_size ;
32
+ BigEndian < uint16_t > checksum ;
33
+ BigEndian < uint16_t > urgent_pointer ;
34
+ uint8_t data [];
35
+
36
+ [[nodiscard ]] uint8_t data_offset () const { return (flags_and_offset .val () & 0xf000 ) >> 12 ; }
37
+ void set_data_offset (uint8_t offset ) { flags_and_offset = (flags_and_offset & 0xfff ) | ((uint16_t ) offset ) << 12 ; }
38
+
39
+ [[nodiscard ]] uint8_t flags () const { return flags_and_offset .val () & 0x01ff ; }
40
+ void set_flags (uint16_t flags ) { flags_and_offset = (flags_and_offset & ~0x01ff ) | (flags & 0x1ff ); }
41
+
42
+ [[nodiscard ]] const uint8_t * payload () const { return ((const uint8_t * ) this ) + (data_offset () * sizeof (uint32_t )); }
43
+ [[nodiscard ]] uint8_t * payload () { return ((uint8_t * ) this ) + (data_offset () * sizeof (uint32_t )); }
44
+
45
+ inline BigEndian < uint16_t > calculate_checksum (const IPv4Address & src , const IPv4Address & dest , size_t payload_size ) {
46
+ union PseudoHeader {
47
+ struct __attribute__((packed )) {
48
+ IPv4Address src ;
49
+ IPv4Address dest ;
50
+ uint8_t zero ;
51
+ uint8_t proto ;
52
+ BigEndian < uint16_t > payload_size ;
53
+ } data ;
54
+ uint16_t raw [6 ] = {0 };
55
+ };
56
+ static_assert (sizeof (PseudoHeader ::data ) == sizeof (PseudoHeader ::raw ));
57
+
58
+ PseudoHeader pheader = {
59
+ .data = {
60
+ .src = src ,
61
+ .dest = dest ,
62
+ .zero = 0 ,
63
+ .proto = IPv4Proto ::TCP ,
64
+ .payload_size = data_offset () * sizeof (uint32_t ) + payload_size
65
+ }
66
+ };
67
+
68
+ uint32_t sum = 0 ;
69
+
70
+ // Checksum of pseudo header
71
+ auto* ptr = (uint16_t * ) pheader .raw ;
72
+ for (size_t i = 0 ; i < sizeof (pheader ) / sizeof (uint16_t ); i ++ ) {
73
+ sum += as_big_endian (ptr [i ]);
74
+ if (sum > 0xffff )
75
+ sum = (sum >> 16 ) + (sum & 0xffff );
76
+ }
77
+
78
+ // Checksum of segment header
79
+ const void * selfptr = this ; // Necessary to suppress alignment errors
80
+ ptr = (uint16_t * ) selfptr ;
81
+ for (size_t i = 0 ; i < (data_offset () * sizeof (uint32_t )) / sizeof (uint16_t ); i ++ ) {
82
+ sum += as_big_endian (ptr [i ]);
83
+ if (sum > 0xffff )
84
+ sum = (sum >> 16 ) + (sum & 0xffff );
85
+ }
86
+
87
+ // Checksum of payload
88
+ ptr = (uint16_t * ) payload ();
89
+ for (size_t i = 0 ; i < payload_size / sizeof (uint16_t ); i ++ ) {
90
+ sum += as_big_endian (ptr [i ]);
91
+ if (sum > 0xffff )
92
+ sum = (sum >> 16 ) + (sum & 0xffff );
93
+ }
94
+
95
+ // Pad
96
+ if (payload_size % 2 != 0 ) {
97
+ sum += ((uint32_t ) payload ()[payload_size - 1 ]) << 8 ;
98
+ if (sum > 0xffff )
99
+ sum = (sum >> 16 ) + (sum & 0xffff );
100
+ }
101
+
102
+ return ~(sum & 0xffff );
103
+ }
104
+ } __attribute__((packed ));
105
+
106
+ static_assert (sizeof (TCPSegment ) == 20 );
0 commit comments