/src/suricata7/src/decode-tcp.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2013 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \ingroup decode |
20 | | * |
21 | | * @{ |
22 | | */ |
23 | | |
24 | | |
25 | | /** |
26 | | * \file |
27 | | * |
28 | | * \author Victor Julien <victor@inliniac.net> |
29 | | * |
30 | | * Decode TCP |
31 | | */ |
32 | | |
33 | | #include "suricata-common.h" |
34 | | #include "decode-tcp.h" |
35 | | #include "decode.h" |
36 | | #include "decode-events.h" |
37 | | #include "util-unittest.h" |
38 | | #include "util-debug.h" |
39 | | #include "util-optimize.h" |
40 | | #include "flow.h" |
41 | | |
42 | | #define SET_OPTS(dst, src) \ |
43 | 574k | (dst).type = (src).type; \ |
44 | 574k | (dst).len = (src).len; \ |
45 | 574k | (dst).data = (src).data |
46 | | |
47 | | static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) |
48 | 1.90M | { |
49 | 1.90M | uint8_t tcp_opt_cnt = 0; |
50 | 1.90M | TCPOpt tcp_opts[TCP_OPTMAX]; |
51 | | |
52 | 1.90M | uint16_t plen = pktlen; |
53 | 6.81M | while (plen) |
54 | 5.40M | { |
55 | 5.40M | const uint8_t type = *pkt; |
56 | | |
57 | | /* single byte options */ |
58 | 5.40M | if (type == TCP_OPT_EOL) { |
59 | 87.7k | break; |
60 | 5.32M | } else if (type == TCP_OPT_NOP) { |
61 | 2.86M | pkt++; |
62 | 2.86M | plen--; |
63 | | |
64 | | /* multibyte options */ |
65 | 2.86M | } else { |
66 | 2.45M | if (plen < 2) { |
67 | 3.80k | break; |
68 | 3.80k | } |
69 | | |
70 | 2.45M | const uint8_t olen = *(pkt+1); |
71 | | |
72 | | /* we already know that the total options len is valid, |
73 | | * so here the len of the specific option must be bad. |
74 | | * Also check for invalid lengths 0 and 1. */ |
75 | 2.45M | if (unlikely(olen > plen || olen < 2)) { |
76 | 403k | ENGINE_SET_INVALID_EVENT(p, TCP_OPT_INVALID_LEN); |
77 | 403k | return; |
78 | 403k | } |
79 | | |
80 | 2.04M | tcp_opts[tcp_opt_cnt].type = type; |
81 | 2.04M | tcp_opts[tcp_opt_cnt].len = olen; |
82 | 2.04M | tcp_opts[tcp_opt_cnt].data = (olen > 2) ? (pkt+2) : NULL; |
83 | | |
84 | | /* we are parsing the most commonly used opts to prevent |
85 | | * us from having to walk the opts list for these all the |
86 | | * time. */ |
87 | 2.04M | switch (type) { |
88 | 162k | case TCP_OPT_WS: |
89 | 162k | if (olen != TCP_OPT_WS_LEN) { |
90 | 8.59k | ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); |
91 | 153k | } else { |
92 | 153k | if (p->tcpvars.ws.type != 0) { |
93 | 6.17k | ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); |
94 | 147k | } else { |
95 | 147k | SET_OPTS(p->tcpvars.ws, tcp_opts[tcp_opt_cnt]); |
96 | 147k | } |
97 | 153k | } |
98 | 162k | break; |
99 | 272k | case TCP_OPT_MSS: |
100 | 272k | if (olen != TCP_OPT_MSS_LEN) { |
101 | 38.7k | ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); |
102 | 234k | } else { |
103 | 234k | if (p->tcpvars.mss.type != 0) { |
104 | 992 | ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); |
105 | 233k | } else { |
106 | 233k | SET_OPTS(p->tcpvars.mss, tcp_opts[tcp_opt_cnt]); |
107 | 233k | } |
108 | 234k | } |
109 | 272k | break; |
110 | 180k | case TCP_OPT_SACKOK: |
111 | 180k | if (olen != TCP_OPT_SACKOK_LEN) { |
112 | 2.87k | ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); |
113 | 177k | } else { |
114 | 177k | if (p->tcpvars.sackok.type != 0) { |
115 | 10.6k | ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); |
116 | 167k | } else { |
117 | 167k | SET_OPTS(p->tcpvars.sackok, tcp_opts[tcp_opt_cnt]); |
118 | 167k | } |
119 | 177k | } |
120 | 180k | break; |
121 | 1.35M | case TCP_OPT_TS: |
122 | 1.35M | if (olen != TCP_OPT_TS_LEN) { |
123 | 10.8k | ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); |
124 | 1.34M | } else { |
125 | 1.34M | if (p->tcpvars.ts_set) { |
126 | 117 | ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); |
127 | 1.34M | } else { |
128 | 1.34M | uint32_t values[2]; |
129 | 1.34M | memcpy(&values, tcp_opts[tcp_opt_cnt].data, sizeof(values)); |
130 | 1.34M | p->tcpvars.ts_val = SCNtohl(values[0]); |
131 | 1.34M | p->tcpvars.ts_ecr = SCNtohl(values[1]); |
132 | 1.34M | p->tcpvars.ts_set = true; |
133 | 1.34M | } |
134 | 1.34M | } |
135 | 1.35M | break; |
136 | 22.4k | case TCP_OPT_SACK: |
137 | 22.4k | SCLogDebug("SACK option, len %u", olen); |
138 | 22.4k | if ((olen != 2) && |
139 | 17.6k | (olen < TCP_OPT_SACK_MIN_LEN || |
140 | 16.6k | olen > TCP_OPT_SACK_MAX_LEN || |
141 | 16.4k | !((olen - 2) % 8 == 0))) |
142 | 1.30k | { |
143 | 1.30k | ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); |
144 | 21.0k | } else { |
145 | 21.0k | if (p->tcpvars.sack.type != 0) { |
146 | 3.52k | ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); |
147 | 17.5k | } else { |
148 | 17.5k | SET_OPTS(p->tcpvars.sack, tcp_opts[tcp_opt_cnt]); |
149 | 17.5k | } |
150 | 21.0k | } |
151 | 22.4k | break; |
152 | 14.0k | case TCP_OPT_TFO: |
153 | 14.0k | SCLogDebug("TFO option, len %u", olen); |
154 | 14.0k | if ((olen != 2) && (olen < TCP_OPT_TFO_MIN_LEN || olen > TCP_OPT_TFO_MAX_LEN || |
155 | 7.25k | !(((olen - 2) & 0x1) == 0))) { |
156 | 3.77k | ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); |
157 | 10.2k | } else { |
158 | 10.2k | if (p->tcpvars.tfo.type != 0) { |
159 | 1.78k | ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); |
160 | 8.47k | } else { |
161 | 8.47k | SET_OPTS(p->tcpvars.tfo, tcp_opts[tcp_opt_cnt]); |
162 | 8.47k | } |
163 | 10.2k | } |
164 | 14.0k | break; |
165 | | /* experimental options, could be TFO */ |
166 | 986 | case TCP_OPT_EXP1: |
167 | 4.80k | case TCP_OPT_EXP2: |
168 | 4.80k | SCLogDebug("TCP EXP option, len %u", olen); |
169 | 4.80k | if (olen == 4 || olen == 12) { |
170 | 2.56k | uint16_t magic = SCNtohs(*(uint16_t *)tcp_opts[tcp_opt_cnt].data); |
171 | 2.56k | if (magic == 0xf989) { |
172 | 1.50k | if (p->tcpvars.tfo.type != 0) { |
173 | 802 | ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); |
174 | 802 | } else { |
175 | 701 | SET_OPTS(p->tcpvars.tfo, tcp_opts[tcp_opt_cnt]); |
176 | 701 | p->tcpvars.tfo.type = TCP_OPT_TFO; // treat as regular TFO |
177 | 701 | } |
178 | 1.50k | } |
179 | 2.56k | } else { |
180 | 2.24k | ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); |
181 | 2.24k | } |
182 | 4.80k | break; |
183 | | /* RFC 2385 MD5 option */ |
184 | 1.49k | case TCP_OPT_MD5: |
185 | 1.49k | SCLogDebug("MD5 option, len %u", olen); |
186 | 1.49k | if (olen != 18) { |
187 | 1.42k | ENGINE_SET_INVALID_EVENT(p,TCP_OPT_INVALID_LEN); |
188 | 1.42k | } else { |
189 | | /* we can't validate the option as the key is out of band */ |
190 | 70 | p->tcpvars.md5_option_present = true; |
191 | 70 | } |
192 | 1.49k | break; |
193 | | /* RFC 5925 AO option */ |
194 | 587 | case TCP_OPT_AO: |
195 | 587 | SCLogDebug("AU option, len %u", olen); |
196 | 587 | if (olen < 4) { |
197 | 185 | ENGINE_SET_INVALID_EVENT(p,TCP_OPT_INVALID_LEN); |
198 | 402 | } else { |
199 | | /* we can't validate the option as the key is out of band */ |
200 | 402 | p->tcpvars.ao_option_present = true; |
201 | 402 | } |
202 | 587 | break; |
203 | 2.04M | } |
204 | | |
205 | 2.04M | pkt += olen; |
206 | 2.04M | plen -= olen; |
207 | 2.04M | tcp_opt_cnt++; |
208 | 2.04M | } |
209 | 5.40M | } |
210 | 1.90M | } |
211 | | |
212 | | static int DecodeTCPPacket(ThreadVars *tv, Packet *p, const uint8_t *pkt, uint16_t len) |
213 | 7.22M | { |
214 | 7.22M | if (unlikely(len < TCP_HEADER_LEN)) { |
215 | 3.98k | ENGINE_SET_INVALID_EVENT(p, TCP_PKT_TOO_SMALL); |
216 | 3.98k | return -1; |
217 | 3.98k | } |
218 | | |
219 | 7.22M | p->tcph = (TCPHdr *)pkt; |
220 | | |
221 | 7.22M | uint8_t hlen = TCP_GET_HLEN(p); |
222 | 7.22M | if (unlikely(len < hlen)) { |
223 | 58.2k | ENGINE_SET_INVALID_EVENT(p, TCP_HLEN_TOO_SMALL); |
224 | 58.2k | return -1; |
225 | 58.2k | } |
226 | | |
227 | 7.16M | uint8_t tcp_opt_len = hlen - TCP_HEADER_LEN; |
228 | 7.16M | if (unlikely(tcp_opt_len > TCP_OPTLENMAX)) { |
229 | 105k | ENGINE_SET_INVALID_EVENT(p, TCP_INVALID_OPTLEN); |
230 | 105k | return -1; |
231 | 105k | } |
232 | | |
233 | 7.05M | if (likely(tcp_opt_len > 0)) { |
234 | 1.90M | DecodeTCPOptions(p, pkt + TCP_HEADER_LEN, tcp_opt_len); |
235 | 1.90M | } |
236 | | |
237 | 7.05M | SET_TCP_SRC_PORT(p,&p->sp); |
238 | 7.05M | SET_TCP_DST_PORT(p,&p->dp); |
239 | | |
240 | 7.05M | p->proto = IPPROTO_TCP; |
241 | | |
242 | 7.05M | p->payload = (uint8_t *)pkt + hlen; |
243 | 7.05M | p->payload_len = len - hlen; |
244 | | |
245 | 7.05M | return 0; |
246 | 7.16M | } |
247 | | |
248 | | int DecodeTCP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, |
249 | | const uint8_t *pkt, uint16_t len) |
250 | 7.22M | { |
251 | 7.22M | StatsIncr(tv, dtv->counter_tcp); |
252 | | |
253 | 7.22M | if (unlikely(DecodeTCPPacket(tv, p, pkt,len) < 0)) { |
254 | 167k | SCLogDebug("invalid TCP packet"); |
255 | 167k | CLEAR_TCP_PACKET(p); |
256 | 167k | return TM_ECODE_FAILED; |
257 | 167k | } |
258 | | |
259 | | /* update counters */ |
260 | 7.05M | if ((p->tcph->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) { |
261 | 274k | StatsIncr(tv, dtv->counter_tcp_synack); |
262 | 6.78M | } else if (p->tcph->th_flags & (TH_SYN)) { |
263 | 161k | StatsIncr(tv, dtv->counter_tcp_syn); |
264 | 161k | } |
265 | 7.05M | if (p->tcph->th_flags & (TH_RST)) { |
266 | 138k | StatsIncr(tv, dtv->counter_tcp_rst); |
267 | 138k | } |
268 | 7.05M | if (p->tcph->th_flags & (TH_URG)) { |
269 | 287k | StatsIncr(tv, dtv->counter_tcp_urg); |
270 | 287k | } |
271 | | #ifdef DEBUG |
272 | | SCLogDebug("TCP sp: %" PRIu32 " -> dp: %" PRIu32 " - HLEN: %" PRIu32 " LEN: %" PRIu32 " %s%s%s%s%s%s", |
273 | | GET_TCP_SRC_PORT(p), GET_TCP_DST_PORT(p), TCP_GET_HLEN(p), len, |
274 | | TCP_HAS_SACKOK(p) ? "SACKOK " : "", TCP_HAS_SACK(p) ? "SACK " : "", |
275 | | TCP_HAS_WSCALE(p) ? "WS " : "", TCP_HAS_TS(p) ? "TS " : "", |
276 | | TCP_HAS_MSS(p) ? "MSS " : "", TCP_HAS_TFO(p) ? "TFO " : ""); |
277 | | #endif |
278 | | |
279 | 7.05M | FlowSetupPacket(p); |
280 | | |
281 | 7.05M | return TM_ECODE_OK; |
282 | 7.22M | } |
283 | | |
284 | | #ifdef UNITTESTS |
285 | | #include "util-unittest-helper.h" |
286 | | #include "packet.h" |
287 | | |
288 | | static int TCPCalculateValidChecksumtest01(void) |
289 | | { |
290 | | uint16_t csum = 0; |
291 | | |
292 | | uint8_t raw_ipshdr[] = { |
293 | | 0x40, 0x8e, 0x7e, 0xb2, 0xc0, 0xa8, 0x01, 0x03}; |
294 | | |
295 | | uint8_t raw_tcp[] = { |
296 | | 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c, |
297 | | 0xcf, 0x0d, 0x21, 0x80, 0xa0, 0x12, 0x16, 0xa0, |
298 | | 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, |
299 | | 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73, |
300 | | 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 02}; |
301 | | |
302 | | csum = *( ((uint16_t *)raw_tcp) + 8); |
303 | | |
304 | | FAIL_IF(TCPChecksum((uint16_t *)raw_ipshdr, |
305 | | (uint16_t *)raw_tcp, sizeof(raw_tcp), csum) != 0); |
306 | | PASS; |
307 | | } |
308 | | |
309 | | static int TCPCalculateInvalidChecksumtest02(void) |
310 | | { |
311 | | uint16_t csum = 0; |
312 | | |
313 | | uint8_t raw_ipshdr[] = { |
314 | | 0x40, 0x8e, 0x7e, 0xb2, 0xc0, 0xa8, 0x01, 0x03}; |
315 | | |
316 | | uint8_t raw_tcp[] = { |
317 | | 0x00, 0x50, 0x8e, 0x16, 0x0d, 0x59, 0xcd, 0x3c, |
318 | | 0xcf, 0x0d, 0x21, 0x80, 0xa0, 0x12, 0x16, 0xa0, |
319 | | 0xfa, 0x03, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, |
320 | | 0x04, 0x02, 0x08, 0x0a, 0x6e, 0x18, 0x78, 0x73, |
321 | | 0x01, 0x71, 0x74, 0xde, 0x01, 0x03, 0x03, 03}; |
322 | | |
323 | | csum = *( ((uint16_t *)raw_tcp) + 8); |
324 | | |
325 | | FAIL_IF(TCPChecksum((uint16_t *) raw_ipshdr, |
326 | | (uint16_t *)raw_tcp, sizeof(raw_tcp), csum) == 0); |
327 | | PASS; |
328 | | } |
329 | | |
330 | | static int TCPV6CalculateValidChecksumtest03(void) |
331 | | { |
332 | | uint16_t csum = 0; |
333 | | |
334 | | static uint8_t raw_ipv6[] = { |
335 | | 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00, |
336 | | 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00, |
337 | | 0x00, 0x00, 0x00, 0x20, 0x06, 0x40, 0x3f, 0xfe, |
338 | | 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, |
339 | | 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe, |
340 | | 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0, |
341 | | 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0x03, 0xfe, |
342 | | 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d, 0x0c, 0x7a, |
343 | | 0x08, 0x77, 0x80, 0x10, 0x21, 0x5c, 0xc2, 0xf1, |
344 | | 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08, |
345 | | 0xca, 0x5a, 0x00, 0x01, 0x69, 0x27}; |
346 | | |
347 | | csum = *( ((uint16_t *)(raw_ipv6 + 70))); |
348 | | |
349 | | FAIL_IF(TCPV6Checksum((uint16_t *)(raw_ipv6 + 14 + 8), |
350 | | (uint16_t *)(raw_ipv6 + 54), 32, csum) != 0); |
351 | | PASS; |
352 | | } |
353 | | |
354 | | static int TCPV6CalculateInvalidChecksumtest04(void) |
355 | | { |
356 | | uint16_t csum = 0; |
357 | | |
358 | | static uint8_t raw_ipv6[] = { |
359 | | 0x00, 0x60, 0x97, 0x07, 0x69, 0xea, 0x00, 0x00, |
360 | | 0x86, 0x05, 0x80, 0xda, 0x86, 0xdd, 0x60, 0x00, |
361 | | 0x00, 0x00, 0x00, 0x20, 0x06, 0x40, 0x3f, 0xfe, |
362 | | 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, |
363 | | 0x86, 0xff, 0xfe, 0x05, 0x80, 0xda, 0x3f, 0xfe, |
364 | | 0x05, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02, 0xc0, |
365 | | 0xdf, 0xff, 0xfe, 0x47, 0x03, 0x3e, 0x03, 0xfe, |
366 | | 0x00, 0x16, 0xd6, 0x76, 0xf5, 0x2d, 0x0c, 0x7a, |
367 | | 0x08, 0x77, 0x80, 0x10, 0x21, 0x5c, 0xc2, 0xf1, |
368 | | 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x08, |
369 | | 0xca, 0x5a, 0x00, 0x01, 0x69, 0x28}; |
370 | | |
371 | | csum = *( ((uint16_t *)(raw_ipv6 + 70))); |
372 | | |
373 | | FAIL_IF(TCPV6Checksum((uint16_t *)(raw_ipv6 + 14 + 8), |
374 | | (uint16_t *)(raw_ipv6 + 54), 32, csum) == 0); |
375 | | PASS; |
376 | | } |
377 | | |
378 | | /** \test Get the wscale of 2 */ |
379 | | static int TCPGetWscaleTest01(void) |
380 | | { |
381 | | int retval = 0; |
382 | | static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x58, |
383 | | 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0xd0, |
384 | | 0x8a, 0xaf, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, |
385 | | 0x04, 0x02, 0x08, 0x0a, 0x00, 0x62, 0x88, 0x28, |
386 | | 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02}; |
387 | | Packet *p = PacketGetFromAlloc(); |
388 | | if (unlikely(p == NULL)) |
389 | | return 0; |
390 | | IPV4Hdr ip4h; |
391 | | ThreadVars tv; |
392 | | DecodeThreadVars dtv; |
393 | | |
394 | | memset(&tv, 0, sizeof(ThreadVars)); |
395 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
396 | | memset(&ip4h, 0, sizeof(IPV4Hdr)); |
397 | | |
398 | | p->src.family = AF_INET; |
399 | | p->dst.family = AF_INET; |
400 | | p->ip4h = &ip4h; |
401 | | |
402 | | |
403 | | FlowInitConfig(FLOW_QUIET); |
404 | | DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp)); |
405 | | |
406 | | if (p->tcph == NULL) { |
407 | | printf("tcp packet decode failed: "); |
408 | | goto end; |
409 | | } |
410 | | |
411 | | uint8_t wscale = TCP_GET_WSCALE(p); |
412 | | if (wscale != 2) { |
413 | | printf("wscale %"PRIu8", expected 2: ", wscale); |
414 | | goto end; |
415 | | } |
416 | | |
417 | | retval = 1; |
418 | | end: |
419 | | PacketRecycle(p); |
420 | | FlowShutdown(); |
421 | | SCFree(p); |
422 | | return retval; |
423 | | } |
424 | | |
425 | | /** \test Get the wscale of 15, so see if return 0 properly */ |
426 | | static int TCPGetWscaleTest02(void) |
427 | | { |
428 | | int retval = 0; |
429 | | static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x58, |
430 | | 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0xd0, |
431 | | 0x8a, 0xaf, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, |
432 | | 0x04, 0x02, 0x08, 0x0a, 0x00, 0x62, 0x88, 0x28, |
433 | | 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x0f}; |
434 | | Packet *p = PacketGetFromAlloc(); |
435 | | if (unlikely(p == NULL)) |
436 | | return 0; |
437 | | IPV4Hdr ip4h; |
438 | | ThreadVars tv; |
439 | | DecodeThreadVars dtv; |
440 | | |
441 | | memset(&tv, 0, sizeof(ThreadVars)); |
442 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
443 | | memset(&ip4h, 0, sizeof(IPV4Hdr)); |
444 | | |
445 | | p->src.family = AF_INET; |
446 | | p->dst.family = AF_INET; |
447 | | p->ip4h = &ip4h; |
448 | | |
449 | | FlowInitConfig(FLOW_QUIET); |
450 | | DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp)); |
451 | | |
452 | | if (p->tcph == NULL) { |
453 | | printf("tcp packet decode failed: "); |
454 | | goto end; |
455 | | } |
456 | | |
457 | | uint8_t wscale = TCP_GET_WSCALE(p); |
458 | | if (wscale != 0) { |
459 | | printf("wscale %"PRIu8", expected 0: ", wscale); |
460 | | goto end; |
461 | | } |
462 | | |
463 | | retval = 1; |
464 | | end: |
465 | | PacketRecycle(p); |
466 | | FlowShutdown(); |
467 | | SCFree(p); |
468 | | return retval; |
469 | | } |
470 | | |
471 | | /** \test Get the wscale, but it's missing, so see if return 0 properly */ |
472 | | static int TCPGetWscaleTest03(void) |
473 | | { |
474 | | int retval = 0; |
475 | | static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x59, |
476 | | 0xdd, 0xa3, 0x6f, 0xf8, 0x80, 0x10, 0x05, 0xb4, |
477 | | 0x7c, 0x70, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, |
478 | | 0x00, 0x62, 0x88, 0x9e, 0x00, 0x00, 0x00, 0x00}; |
479 | | Packet *p = PacketGetFromAlloc(); |
480 | | if (unlikely(p == NULL)) |
481 | | return 0; |
482 | | IPV4Hdr ip4h; |
483 | | ThreadVars tv; |
484 | | DecodeThreadVars dtv; |
485 | | |
486 | | memset(&tv, 0, sizeof(ThreadVars)); |
487 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
488 | | memset(&ip4h, 0, sizeof(IPV4Hdr)); |
489 | | |
490 | | p->src.family = AF_INET; |
491 | | p->dst.family = AF_INET; |
492 | | p->ip4h = &ip4h; |
493 | | |
494 | | FlowInitConfig(FLOW_QUIET); |
495 | | DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp)); |
496 | | |
497 | | if (p->tcph == NULL) { |
498 | | printf("tcp packet decode failed: "); |
499 | | goto end; |
500 | | } |
501 | | |
502 | | uint8_t wscale = TCP_GET_WSCALE(p); |
503 | | if (wscale != 0) { |
504 | | printf("wscale %"PRIu8", expected 0: ", wscale); |
505 | | goto end; |
506 | | } |
507 | | |
508 | | retval = 1; |
509 | | end: |
510 | | PacketRecycle(p); |
511 | | FlowShutdown(); |
512 | | SCFree(p); |
513 | | return retval; |
514 | | } |
515 | | |
516 | | static int TCPGetSackTest01(void) |
517 | | { |
518 | | int retval = 0; |
519 | | static uint8_t raw_tcp[] = { |
520 | | 0x00, 0x50, 0x06, 0xa6, 0xfa, 0x87, 0x0b, 0xf5, |
521 | | 0xf1, 0x59, 0x02, 0xe0, 0xa0, 0x10, 0x3e, 0xbc, |
522 | | 0x1d, 0xe7, 0x00, 0x00, 0x01, 0x01, 0x05, 0x12, |
523 | | 0xf1, 0x59, 0x13, 0xfc, 0xf1, 0x59, 0x1f, 0x64, |
524 | | 0xf1, 0x59, 0x08, 0x94, 0xf1, 0x59, 0x0e, 0x48 }; |
525 | | static uint8_t raw_tcp_sack[] = { |
526 | | 0xf1, 0x59, 0x13, 0xfc, 0xf1, 0x59, 0x1f, 0x64, |
527 | | 0xf1, 0x59, 0x08, 0x94, 0xf1, 0x59, 0x0e, 0x48 }; |
528 | | Packet *p = PacketGetFromAlloc(); |
529 | | if (unlikely(p == NULL)) |
530 | | return 0; |
531 | | IPV4Hdr ip4h; |
532 | | ThreadVars tv; |
533 | | DecodeThreadVars dtv; |
534 | | |
535 | | memset(&tv, 0, sizeof(ThreadVars)); |
536 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
537 | | memset(&ip4h, 0, sizeof(IPV4Hdr)); |
538 | | |
539 | | p->src.family = AF_INET; |
540 | | p->dst.family = AF_INET; |
541 | | p->ip4h = &ip4h; |
542 | | |
543 | | FlowInitConfig(FLOW_QUIET); |
544 | | DecodeTCP(&tv, &dtv, p, raw_tcp, sizeof(raw_tcp)); |
545 | | |
546 | | if (p->tcph == NULL) { |
547 | | printf("tcp packet decode failed: "); |
548 | | goto end; |
549 | | } |
550 | | |
551 | | if (!TCP_HAS_SACK(p)) { |
552 | | printf("tcp packet sack not decoded: "); |
553 | | goto end; |
554 | | } |
555 | | |
556 | | int sack = TCP_GET_SACK_CNT(p); |
557 | | if (sack != 2) { |
558 | | printf("expected 2 sack records, got %u: ", TCP_GET_SACK_CNT(p)); |
559 | | goto end; |
560 | | } |
561 | | |
562 | | const uint8_t *sackptr = TCP_GET_SACK_PTR(p); |
563 | | if (sackptr == NULL) { |
564 | | printf("no sack data: "); |
565 | | goto end; |
566 | | } |
567 | | |
568 | | if (memcmp(sackptr, raw_tcp_sack, 16) != 0) { |
569 | | printf("malformed sack data: "); |
570 | | goto end; |
571 | | } |
572 | | |
573 | | retval = 1; |
574 | | end: |
575 | | PacketRecycle(p); |
576 | | FlowShutdown(); |
577 | | SCFree(p); |
578 | | return retval; |
579 | | } |
580 | | #endif /* UNITTESTS */ |
581 | | |
582 | | void DecodeTCPRegisterTests(void) |
583 | 0 | { |
584 | | #ifdef UNITTESTS |
585 | | UtRegisterTest("TCPCalculateValidChecksumtest01", |
586 | | TCPCalculateValidChecksumtest01); |
587 | | UtRegisterTest("TCPCalculateInvalidChecksumtest02", |
588 | | TCPCalculateInvalidChecksumtest02); |
589 | | UtRegisterTest("TCPV6CalculateValidChecksumtest03", |
590 | | TCPV6CalculateValidChecksumtest03); |
591 | | UtRegisterTest("TCPV6CalculateInvalidChecksumtest04", |
592 | | TCPV6CalculateInvalidChecksumtest04); |
593 | | UtRegisterTest("TCPGetWscaleTest01", TCPGetWscaleTest01); |
594 | | UtRegisterTest("TCPGetWscaleTest02", TCPGetWscaleTest02); |
595 | | UtRegisterTest("TCPGetWscaleTest03", TCPGetWscaleTest03); |
596 | | UtRegisterTest("TCPGetSackTest01", TCPGetSackTest01); |
597 | | #endif /* UNITTESTS */ |
598 | 0 | } |
599 | | /** |
600 | | * @} |
601 | | */ |