-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathTCP.3d
281 lines (250 loc) · 8.9 KB
/
TCP.3d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
Specifying the format of TCP headers, from RFC 793, augmented to
support more recent TCP option kinds and TCP flags.
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 | |N|C|E|U|A|P|R|S|F| |
| Offset| Rese|S|W|C|R|C|S|S|Y|I| Window |
| | rved| |R|E|G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Parsing variable length data is hard and TCP option parsing is no
exception.
Even in code that's been in the Linux TCP/IP stack for 20 years or
more, memory safety bugs have been found and patched, as recently as
in 2019. See:
https://github.com/torvalds/linux/commit/9609dad263f8bea347f41fddca29353dbf8a7d37
This file contains a 3D specification of the TCP headers.
The main interesting bit is the specification of variable length TCP options in the header,
and reading them into a parse tree of the programmer's choosing.
Below we parse the options into a struct similar to the one used in the Linux kernel
*/
typedef UINT16BE PORT;
typedef UINT32BE SEQ_NUMBER;
typedef UINT32BE ACK_NUMBER;
typedef UINT16BE WINDOW;
typedef UINT16BE CHECK_SUM;
typedef UINT16BE URGENT_PTR;
#define OPTION_KIND_END_OF_OPTION_LIST 0x00
#define OPTION_KIND_NO_OPERATION 0x01
#define OPTION_KIND_MAX_SEG_SIZE 0x02
#define OPTION_KIND_WINDOW_SCALE 0x03
#define OPTION_KIND_SACK_PERMITTED 0x04
#define OPTION_KIND_SACK 0x05
#define OPTION_KIND_TIMESTAMP 0x08
#define TCP_MAX_WSCALE 14
#define TCP_SACK_SEEN 1
//
// We parse TCP options into the following struct,
// similar to the one defined in the TCP/IP implementation in the Linux kernel
// see struct tcp_options_received in include/linux/tcp.h in the Linux kernel code
//
output
typedef struct _TCP_OPTIONS_RECEIVED
{
UINT32 TS_RECENT_STAMP;
UINT32 TS_RECENT;
UINT32 RCV_TSVAL;
UINT32 RCV_TSECR;
UINT16 SAW_TSTAMP : 1;
UINT16 TSTAMP_OK : 1;
UINT16 DSACK : 1;
UINT16 WSCALE_OK : 1;
UINT16 SACK_OK : 3;
UINT16 SMC_OK : 1;
UINT16 SND_WSCALE : 4;
UINT16 RCV_WSCALE : 4;
UINT8 SAW_UNKNOWN : 1;
UINT8 UNUSED : 7;
UINT8 NUM_SACKS;
UINT16 USER_MSS;
UINT16 MSS_CLAMP;
} TCP_OPTIONS_RECEIVED;
typedef struct _WINDOW_SCALE_PAYLOAD(Bool WindowScaleAllowed,
mutable TCP_OPTIONS_RECEIVED *OptRx)
where WindowScaleAllowed
{
UINT8 Length
{
Length == 3
};
UINT8 WindowScale
{:act
OptRx->WSCALE_OK = 1;
if((WindowScale <= TCP_MAX_WSCALE)){
OptRx->SND_WSCALE = WindowScale;
} else {
OptRx->SND_WSCALE = TCP_MAX_WSCALE;
}};
} WINDOW_SCALE_PAYLOAD;
typedef struct _MAX_SEG_SIZE_PAYLOAD(Bool MaxSegSizeAllowed,
mutable TCP_OPTIONS_RECEIVED *OptRx)
where MaxSegSizeAllowed
{
UINT8 Length
{
Length == 4
};
UINT16BE MaxSegSize
{:act
OptRx->MSS_CLAMP = MaxSegSize;
};
} MAX_SEG_SIZE_PAYLOAD;
typedef struct _TIMESTAMP_PAYLOAD(Bool TimeStampAllowed,
mutable TCP_OPTIONS_RECEIVED *OptRx)
where TimeStampAllowed
{
UINT8 Length
{
Length == 10
};
UINT32BE Tsval;
UINT32BE Tsecr
{:act
OptRx->SAW_TSTAMP = 1;
OptRx->RCV_TSVAL = Tsval;
OptRx->RCV_TSECR = Tsecr;
};
} TIMESTAMP_PAYLOAD;
typedef struct _SackPerm_PAYLOAD(Bool SackPermAllowed,
mutable TCP_OPTIONS_RECEIVED *OptRx)
where SackPermAllowed
{
UINT8 Length
{
Length == 2
};
unit Noop
{:act
OptRx->SACK_OK = TCP_SACK_SEEN;
OptRx->DSACK = 0;
OptRx->NUM_SACKS = 0;};
} SackPermPayload;
typedef struct _SELECTIVE_ACK_PAYLOAD(mutable PUINT8 *SackPayload)
{
UINT8 Length
{
Length == 10 ||
Length == 18 ||
Length == 26 ||
Length == 34
};
UINT8 SelectiveAck[:byte-size (Length - 2)]
{:act
var x = field_ptr;
*SackPayload = x;};
} SELECTIVE_ACK_PAYLOAD;
casetype _OPTION_PAYLOAD(UINT8 OptionKind,
Bool WindowScaleAllowed,
Bool MaxSegSizeAllowed,
Bool TimeStampAllowed,
Bool SackPermAllowed,
mutable PUINT8 *SackPayload,
mutable TCP_OPTIONS_RECEIVED *OptRx)
{
switch(OptionKind)
{
case OPTION_KIND_END_OF_OPTION_LIST:
all_zeros EndOfList;
case OPTION_KIND_NO_OPERATION:
unit Noop;
case OPTION_KIND_MAX_SEG_SIZE:
MAX_SEG_SIZE_PAYLOAD(MaxSegSizeAllowed, OptRx) MaxSegSizePayload;
case OPTION_KIND_WINDOW_SCALE:
WINDOW_SCALE_PAYLOAD(WindowScaleAllowed, OptRx) WindowScalePayload;
case OPTION_KIND_SACK_PERMITTED:
SackPermPayload(SackPermAllowed, OptRx) SackPermittedPayload;
case OPTION_KIND_SACK:
SELECTIVE_ACK_PAYLOAD(SackPayload) SelectiveAckPayload;
case OPTION_KIND_TIMESTAMP:
TIMESTAMP_PAYLOAD(TimeStampAllowed, OptRx) TimestampPayload;
}
} OPTION_PAYLOAD;
typedef struct _OPTION(Bool WindowScaleAllowed,
Bool MaxSegSizeAllowed,
Bool TimeStampAllowed,
Bool SackPermAllowed,
mutable PUINT8 *SackPayload,
mutable TCP_OPTIONS_RECEIVED *OptRx)
{
UINT8 OptionKind;
OPTION_PAYLOAD(OptionKind,
WindowScaleAllowed,
MaxSegSizeAllowed,
TimeStampAllowed,
SackPermAllowed,
SackPayload,
OptRx) OptionPayload;
} OPTION;
/*++
The top-level type of a TCP Header
Arguments:
* UINT32 SegmentLength, the size of the packet,
including both header and data, passed in by the caller
* Estab, a boolean that determines whether certain options are expected in the header
We replicate the logic used in the Linux kernel
* SackPayload, an out pointer to return the SACK option payload to the caller
* OptRx, struct to parse the TCP options into
--*/
entrypoint
typedef struct _TCP_HEADER(UINT32 SegmentLength,
Bool Estab,
mutable PUINT8 *SackPayload,
mutable TCP_OPTIONS_RECEIVED *OptRx)
{
PORT SourcePort;
PORT DestinationPort;
SEQ_NUMBER SeqNumber;
ACK_NUMBER AckNumber;
UINT16BE DataOffset:4
{ //DataOffset is in units of 32 bit words
sizeof(this) == 20 && //Sanity check: static size in bytes of this type, excluding the variable length elements, is 20
sizeof(this) <= DataOffset * 4 && //DataOffset points after the static fields
DataOffset * 4 <= SegmentLength
};
UINT16BE Reserved:3
{
Reserved == 0 //Reserved bytes are unused
};
UINT16BE NS:1;
UINT16BE CWR:1;
UINT16BE ECE:1;
UINT16BE URG:1;
UINT16BE ACK:1
{
AckNumber == 0 ||
ACK == 1 //AckNumber can only be set if the ACK flag is set
} ;
UINT16BE PSH:1;
UINT16BE RST:1;
UINT16BE SYN:1;
UINT16BE FIN:1;
WINDOW Window;
CHECK_SUM CheckSum;
URGENT_PTR UrgentPointer
{
UrgentPointer == 0 ||
URG == 1 //UrgentPointer can only be set if the URG flag is set
};
//The SYN=1 condition indicates when MAX_SEG_SIZE option can be received
//This is an array of options consuming
OPTION(SYN==1,
Estab,
Estab,
SYN==1&&(!Estab),
SackPayload,
OptRx) Options[:byte-size (DataOffset * 4) - sizeof(this)];
UINT8 Data[SegmentLength - (DataOffset * 4)];
} TCP_HEADER;