Coverage Report

Created: 2026-06-30 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */