1 /* Copyright (C) 2007-2011 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 * \file
20 *
21 * \author Victor Julien <victor@inliniac.net>
22 *
23 * Stream engine TCP SACK handling.
24 */
25
26 #include "suricata-common.h"
27 #include "stream-tcp.h"
28 #include "stream-tcp-private.h"
29 #include "stream-tcp-sack.h"
30 #include "util-unittest.h"
31
32 RB_GENERATE(TCPSACK, StreamTcpSackRecord, rb, TcpSackCompare);
33
TcpSackCompare(struct StreamTcpSackRecord * a,struct StreamTcpSackRecord * b)34 int TcpSackCompare(struct StreamTcpSackRecord *a, struct StreamTcpSackRecord *b)
35 {
36 if (SEQ_GT(a->le, b->le))
37 return 1;
38 else if (SEQ_LT(a->le, b->le))
39 return -1;
40 else {
41 if (SEQ_EQ(a->re, b->re))
42 return 0;
43 else if (SEQ_GT(a->re, b->re))
44 return 1;
45 else
46 return -1;
47 }
48 }
49 #ifdef DEBUG
StreamTcpSackPrintList(TcpStream * stream)50 static void StreamTcpSackPrintList(TcpStream *stream)
51 {
52 SCLogDebug("size %u", stream->sack_size);
53 StreamTcpSackRecord *rec = NULL;
54 RB_FOREACH(rec, TCPSACK, &stream->sack_tree) {
55 SCLogDebug("- record %8u - %8u", rec->le, rec->re);
56 }
57 }
58 #endif /* DEBUG */
59
StreamTcpSackRecordAlloc(void)60 static inline StreamTcpSackRecord *StreamTcpSackRecordAlloc(void)
61 {
62 if (StreamTcpCheckMemcap((uint32_t)sizeof(StreamTcpSackRecord)) == 0)
63 return NULL;
64
65 StreamTcpSackRecord *rec = SCMalloc(sizeof(*rec));
66 if (unlikely(rec == NULL))
67 return NULL;
68
69 StreamTcpIncrMemuse((uint64_t)sizeof(*rec));
70 return rec;
71 }
72
StreamTcpSackRecordFree(StreamTcpSackRecord * rec)73 static inline void StreamTcpSackRecordFree(StreamTcpSackRecord *rec)
74 {
75 SCFree(rec);
76 StreamTcpDecrMemuse((uint64_t)sizeof(*rec));
77 }
78
ConsolidateFwd(TcpStream * stream,struct TCPSACK * tree,struct StreamTcpSackRecord * sa)79 static inline void ConsolidateFwd(TcpStream *stream, struct TCPSACK *tree, struct StreamTcpSackRecord *sa)
80 {
81 struct StreamTcpSackRecord *tr, *s = sa;
82 RB_FOREACH_FROM(tr, TCPSACK, s) {
83 if (sa == tr)
84 continue;
85 SCLogDebug("-> (fwd) tr %p %u/%u", tr, tr->le, tr->re);
86
87 if (SEQ_LT(sa->re, tr->le))
88 break; // entirely before
89
90 if (SEQ_GEQ(sa->le, tr->le) && SEQ_LEQ(sa->re, tr->re)) {
91 stream->sack_size -= (tr->re - tr->le);
92 stream->sack_size -= (sa->re - sa->le);
93 sa->re = tr->re;
94 sa->le = tr->le;
95 stream->sack_size += (sa->re - sa->le);
96 SCLogDebug("-> (fwd) tr %p %u/%u REMOVED ECLIPSED2", tr, tr->le, tr->re);
97 TCPSACK_RB_REMOVE(tree, tr);
98 StreamTcpSackRecordFree(tr);
99 /*
100 sa: [ ]
101 tr: [ ]
102 sa: [ ]
103 tr: [ ]
104 sa: [ ]
105 tr: [ ]
106 */
107 } else if (SEQ_LEQ(sa->le, tr->le) && SEQ_GEQ(sa->re, tr->re)) {
108 SCLogDebug("-> (fwd) tr %p %u/%u REMOVED ECLIPSED", tr, tr->le, tr->re);
109 stream->sack_size -= (tr->re - tr->le);
110 TCPSACK_RB_REMOVE(tree, tr);
111 StreamTcpSackRecordFree(tr);
112 /*
113 sa: [ ]
114 tr: [ ]
115 sa: [ ]
116 tr: [ ]
117 */
118 } else if (SEQ_LT(sa->le, tr->le) && // starts before
119 SEQ_GEQ(sa->re, tr->le) && SEQ_LT(sa->re, tr->re) // ends inside
120 ) {
121 // merge
122 stream->sack_size -= (tr->re - tr->le);
123 stream->sack_size -= (sa->re - sa->le);
124 sa->re = tr->re;
125 stream->sack_size += (sa->re - sa->le);
126 SCLogDebug("-> (fwd) tr %p %u/%u REMOVED MERGED", tr, tr->le, tr->re);
127 TCPSACK_RB_REMOVE(tree, tr);
128 StreamTcpSackRecordFree(tr);
129 }
130 }
131 }
132
ConsolidateBackward(TcpStream * stream,struct TCPSACK * tree,struct StreamTcpSackRecord * sa)133 static inline void ConsolidateBackward(TcpStream *stream,
134 struct TCPSACK *tree, struct StreamTcpSackRecord *sa)
135 {
136 struct StreamTcpSackRecord *tr, *s = sa;
137 RB_FOREACH_REVERSE_FROM(tr, TCPSACK, s) {
138 if (sa == tr)
139 continue;
140 SCLogDebug("-> (bwd) tr %p %u/%u", tr, tr->le, tr->re);
141
142 if (SEQ_GT(sa->le, tr->re))
143 break; // entirely after
144 if (SEQ_GEQ(sa->le, tr->le) && SEQ_LEQ(sa->re, tr->re)) {
145 stream->sack_size -= (tr->re - tr->le);
146 stream->sack_size -= (sa->re - sa->le);
147 sa->re = tr->re;
148 sa->le = tr->le;
149 stream->sack_size += (sa->re - sa->le);
150 SCLogDebug("-> (bwd) tr %p %u/%u REMOVED ECLIPSED2", tr, tr->le, tr->re);
151 TCPSACK_RB_REMOVE(tree, tr);
152 StreamTcpSackRecordFree(tr);
153 /*
154 sa: [ ]
155 tr: [ ]
156 sa: [ ]
157 tr: [ ]
158 sa: [ ]
159 tr: [ ]
160 */
161 } else if (SEQ_LEQ(sa->le, tr->le) && SEQ_GEQ(sa->re, tr->re)) {
162 SCLogDebug("-> (bwd) tr %p %u/%u REMOVED ECLIPSED", tr, tr->le, tr->re);
163 stream->sack_size -= (tr->re - tr->le);
164 TCPSACK_RB_REMOVE(tree, tr);
165 StreamTcpSackRecordFree(tr);
166 /*
167 sa: [ ]
168 tr: [ ]
169 sa: [ ]
170 tr: [ ]
171 */
172 } else if (SEQ_GT(sa->le, tr->le) && SEQ_GT(sa->re, tr->re) && SEQ_LEQ(sa->le,tr->re)) {
173 // merge
174 stream->sack_size -= (tr->re - tr->le);
175 stream->sack_size -= (sa->re - sa->le);
176 sa->le = tr->le;
177 stream->sack_size += (sa->re - sa->le);
178 SCLogDebug("-> (bwd) tr %p %u/%u REMOVED MERGED", tr, tr->le, tr->re);
179 TCPSACK_RB_REMOVE(tree, tr);
180 StreamTcpSackRecordFree(tr);
181 }
182 }
183 }
184
Insert(TcpStream * stream,struct TCPSACK * tree,uint32_t le,uint32_t re)185 static int Insert(TcpStream *stream, struct TCPSACK *tree, uint32_t le, uint32_t re)
186 {
187 SCLogDebug("* inserting: %u/%u\n", le, re);
188
189 struct StreamTcpSackRecord *sa = StreamTcpSackRecordAlloc();
190 if (unlikely(sa == NULL))
191 return -1;
192 sa->le = le;
193 sa->re = re;
194 struct StreamTcpSackRecord *res = TCPSACK_RB_INSERT(tree, sa);
195 if (res) {
196 // exact overlap
197 SCLogDebug("* insert failed: exact match in tree with %p %u/%u", res, res->le, res->re);
198 StreamTcpSackRecordFree(sa);
199 return 0;
200 }
201 stream->sack_size += (re - le);
202 ConsolidateBackward(stream, tree, sa);
203 ConsolidateFwd(stream, tree, sa);
204 return 0;
205 }
206
207 /**
208 * \brief insert a SACK range
209 *
210 * \param le left edge in host order
211 * \param re right edge in host order
212 *
213 * \retval 0 all is good
214 * \retval -1 error
215 */
StreamTcpSackInsertRange(TcpStream * stream,uint32_t le,uint32_t re)216 static int StreamTcpSackInsertRange(TcpStream *stream, uint32_t le, uint32_t re)
217 {
218 SCLogDebug("le %u, re %u", le, re);
219 #ifdef DEBUG
220 StreamTcpSackPrintList(stream);
221 #endif
222
223 /* if to the left of last_ack then ignore */
224 if (SEQ_LT(re, stream->last_ack)) {
225 SCLogDebug("too far left. discarding");
226 SCReturnInt(0);
227 }
228 /* if to the right of the tcp window then ignore */
229 if (SEQ_GT(le, (stream->last_ack + stream->window))) {
230 SCLogDebug("too far right. discarding");
231 SCReturnInt(0);
232 }
233
234 if (Insert(stream, &stream->sack_tree, le, re) < 0)
235 SCReturnInt(-1);
236
237 SCReturnInt(0);
238 }
239
240 /**
241 * \brief Update stream with SACK records from a TCP packet.
242 *
243 * \param stream The stream to update.
244 * \param p packet to get the SACK records from
245 *
246 * \retval -1 error
247 * \retval 0 ok
248 */
StreamTcpSackUpdatePacket(TcpStream * stream,Packet * p)249 int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p)
250 {
251 const int records = TCP_GET_SACK_CNT(p);
252 const uint8_t *data = TCP_GET_SACK_PTR(p);
253
254 if (records == 0 || data == NULL)
255 return 0;
256
257 TCPOptSackRecord rec[records], *sack_rec = rec;
258 memcpy(&rec, data, sizeof(TCPOptSackRecord) * records);
259
260 for (int record = 0; record < records; record++) {
261 const uint32_t le = SCNtohl(sack_rec->le);
262 const uint32_t re = SCNtohl(sack_rec->re);
263
264 SCLogDebug("%p last_ack %u, left edge %u, right edge %u", sack_rec,
265 stream->last_ack, le, re);
266
267 if (SEQ_LEQ(re, stream->last_ack)) {
268 SCLogDebug("record before last_ack");
269 goto next;
270 }
271
272 if (SEQ_GT(re, stream->next_win)) {
273 SCLogDebug("record %u:%u beyond next_win %u",
274 le, re, stream->next_win);
275 goto next;
276 }
277
278 if (SEQ_GEQ(le, re)) {
279 SCLogDebug("invalid record: le >= re");
280 goto next;
281 }
282
283 if (StreamTcpSackInsertRange(stream, le, re) == -1) {
284 SCReturnInt(-1);
285 }
286
287 next:
288 sack_rec++;
289 }
290 StreamTcpSackPruneList(stream);
291 #ifdef DEBUG
292 StreamTcpSackPrintList(stream);
293 #endif
294 SCReturnInt(0);
295 }
296
StreamTcpSackPruneList(TcpStream * stream)297 void StreamTcpSackPruneList(TcpStream *stream)
298 {
299 SCEnter();
300
301 StreamTcpSackRecord *rec = NULL, *safe = NULL;
302 RB_FOREACH_SAFE(rec, TCPSACK, &stream->sack_tree, safe) {
303 if (SEQ_LT(rec->re, stream->last_ack)) {
304 SCLogDebug("removing le %u re %u", rec->le, rec->re);
305 stream->sack_size -= (rec->re - rec->le);
306 TCPSACK_RB_REMOVE(&stream->sack_tree, rec);
307 StreamTcpSackRecordFree(rec);
308
309 } else if (SEQ_LT(rec->le, stream->last_ack)) {
310 SCLogDebug("adjusting record to le %u re %u", rec->le, rec->re);
311 /* last ack inside this record, update */
312 stream->sack_size -= (rec->re - rec->le);
313 rec->le = stream->last_ack;
314 stream->sack_size += (rec->re - rec->le);
315 break;
316 } else {
317 SCLogDebug("record beyond last_ack, nothing to do. Bailing out.");
318 break;
319 }
320 }
321 #ifdef DEBUG
322 StreamTcpSackPrintList(stream);
323 #endif
324 SCReturn;
325 }
326
327 /**
328 * \brief Free SACK tree from a stream
329 *
330 * \param stream Stream to cleanup
331 */
StreamTcpSackFreeList(TcpStream * stream)332 void StreamTcpSackFreeList(TcpStream *stream)
333 {
334 SCEnter();
335
336 StreamTcpSackRecord *rec = NULL, *safe = NULL;
337 RB_FOREACH_SAFE(rec, TCPSACK, &stream->sack_tree, safe) {
338 stream->sack_size -= (rec->re - rec->le);
339 TCPSACK_RB_REMOVE(&stream->sack_tree, rec);
340 StreamTcpSackRecordFree(rec);
341 }
342
343 SCReturn;
344 }
345
346
347 #ifdef UNITTESTS
348
349 /**
350 * \test Test the insertion of SACK ranges.
351 *
352 * \retval On success it returns 1 and on failure 0.
353 */
354
StreamTcpSackTest01(void)355 static int StreamTcpSackTest01 (void)
356 {
357 TcpStream stream;
358 memset(&stream, 0, sizeof(stream));
359 stream.window = 100;
360
361 StreamTcpSackInsertRange(&stream, 1, 10);
362 FAIL_IF_NOT(stream.sack_size == 9);
363 StreamTcpSackInsertRange(&stream, 10, 20);
364 FAIL_IF_NOT(stream.sack_size == 19);
365 StreamTcpSackInsertRange(&stream, 10, 20);
366 FAIL_IF_NOT(stream.sack_size == 19);
367 StreamTcpSackInsertRange(&stream, 1, 20);
368 FAIL_IF_NOT(stream.sack_size == 19);
369 #ifdef DEBUG
370 StreamTcpSackPrintList(&stream);
371 #endif /* DEBUG */
372
373 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
374 FAIL_IF_NULL(rec);
375
376 FAIL_IF(rec->le != 1);
377 FAIL_IF(rec->re != 20);
378
379 FAIL_IF(StreamTcpSackedSize(&stream) != 19);
380 StreamTcpSackFreeList(&stream);
381 PASS;
382 }
383
384 /**
385 * \test Test the insertion of SACK ranges.
386 *
387 * \retval On success it returns 1 and on failure 0.
388 */
389
StreamTcpSackTest02(void)390 static int StreamTcpSackTest02 (void)
391 {
392 TcpStream stream;
393 memset(&stream, 0, sizeof(stream));
394 stream.window = 100;
395
396 StreamTcpSackInsertRange(&stream, 10, 20);
397 StreamTcpSackInsertRange(&stream, 1, 20);
398 #ifdef DEBUG
399 StreamTcpSackPrintList(&stream);
400 #endif /* DEBUG */
401
402 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
403 FAIL_IF_NULL(rec);
404
405 FAIL_IF(rec->le != 1);
406 FAIL_IF(rec->re != 20);
407
408 FAIL_IF(StreamTcpSackedSize(&stream) != 19);
409 StreamTcpSackFreeList(&stream);
410 PASS;
411 }
412
413 /**
414 * \test Test the insertion of SACK ranges.
415 *
416 * \retval On success it returns 1 and on failure 0.
417 */
418
StreamTcpSackTest03(void)419 static int StreamTcpSackTest03 (void)
420 {
421 TcpStream stream;
422 memset(&stream, 0, sizeof(stream));
423 stream.window = 100;
424
425 StreamTcpSackInsertRange(&stream, 10, 20);
426 StreamTcpSackInsertRange(&stream, 5, 15);
427 #ifdef DEBUG
428 StreamTcpSackPrintList(&stream);
429 #endif /* DEBUG */
430 StreamTcpSackInsertRange(&stream, 15, 25);
431 #ifdef DEBUG
432 StreamTcpSackPrintList(&stream);
433 #endif /* DEBUG */
434
435 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
436 FAIL_IF_NULL(rec);
437
438 FAIL_IF(rec->le != 5);
439 FAIL_IF(rec->re != 25);
440
441 FAIL_IF(StreamTcpSackedSize(&stream) != 20);
442 StreamTcpSackFreeList(&stream);
443 PASS;
444 }
445
446 /**
447 * \test Test the insertion of SACK ranges.
448 *
449 * \retval On success it returns 1 and on failure 0.
450 */
451
StreamTcpSackTest04(void)452 static int StreamTcpSackTest04 (void)
453 {
454 TcpStream stream;
455 memset(&stream, 0, sizeof(stream));
456 stream.window = 100;
457
458 StreamTcpSackInsertRange(&stream, 0, 20);
459 StreamTcpSackInsertRange(&stream, 30, 50);
460 StreamTcpSackInsertRange(&stream, 10, 25);
461 #ifdef DEBUG
462 StreamTcpSackPrintList(&stream);
463 #endif /* DEBUG */
464
465 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
466 FAIL_IF_NULL(rec);
467
468 FAIL_IF(rec->le != 0);
469 FAIL_IF(rec->re != 25);
470
471 FAIL_IF(StreamTcpSackedSize(&stream) != 45);
472 StreamTcpSackFreeList(&stream);
473 PASS;
474 }
475
476 /**
477 * \test Test the insertion of SACK ranges.
478 *
479 * \retval On success it returns 1 and on failure 0.
480 */
481
StreamTcpSackTest05(void)482 static int StreamTcpSackTest05 (void)
483 {
484 TcpStream stream;
485 memset(&stream, 0, sizeof(stream));
486 stream.window = 100;
487
488 StreamTcpSackInsertRange(&stream, 0, 20);
489 StreamTcpSackInsertRange(&stream, 30, 50);
490 StreamTcpSackInsertRange(&stream, 10, 35);
491 #ifdef DEBUG
492 StreamTcpSackPrintList(&stream);
493 #endif /* DEBUG */
494
495 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
496 FAIL_IF_NULL(rec);
497
498 FAIL_IF(rec->le != 0);
499 FAIL_IF(rec->re != 50);
500
501 FAIL_IF(StreamTcpSackedSize(&stream) != 50);
502 StreamTcpSackFreeList(&stream);
503 PASS;
504 }
505
506 /**
507 * \test Test the insertion of SACK ranges.
508 *
509 * \retval On success it returns 1 and on failure 0.
510 */
511
StreamTcpSackTest06(void)512 static int StreamTcpSackTest06 (void)
513 {
514 TcpStream stream;
515 memset(&stream, 0, sizeof(stream));
516 stream.window = 100;
517
518 StreamTcpSackInsertRange(&stream, 0, 9);
519 StreamTcpSackInsertRange(&stream, 11, 19);
520 StreamTcpSackInsertRange(&stream, 21, 29);
521 StreamTcpSackInsertRange(&stream, 31, 39);
522 StreamTcpSackInsertRange(&stream, 0, 40);
523 #ifdef DEBUG
524 StreamTcpSackPrintList(&stream);
525 #endif /* DEBUG */
526
527 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
528 FAIL_IF_NULL(rec);
529
530 FAIL_IF(rec->le != 0);
531 FAIL_IF(rec->re != 40);
532
533 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
534 StreamTcpSackFreeList(&stream);
535 PASS;
536 }
537
538 /**
539 * \test Test the pruning of SACK ranges.
540 *
541 * \retval On success it returns 1 and on failure 0.
542 */
543
StreamTcpSackTest07(void)544 static int StreamTcpSackTest07 (void)
545 {
546 TcpStream stream;
547 memset(&stream, 0, sizeof(stream));
548 stream.window = 100;
549
550 StreamTcpSackInsertRange(&stream, 0, 9);
551 StreamTcpSackInsertRange(&stream, 11, 19);
552 StreamTcpSackInsertRange(&stream, 21, 29);
553 StreamTcpSackInsertRange(&stream, 31, 39);
554 StreamTcpSackInsertRange(&stream, 0, 40);
555 #ifdef DEBUG
556 StreamTcpSackPrintList(&stream);
557 #endif /* DEBUG */
558
559 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
560 FAIL_IF_NULL(rec);
561 FAIL_IF(rec->le != 0);
562 FAIL_IF(rec->re != 40);
563 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
564
565 stream.last_ack = 10;
566 StreamTcpSackPruneList(&stream);
567 FAIL_IF(StreamTcpSackedSize(&stream) != 30);
568
569 StreamTcpSackFreeList(&stream);
570 PASS;
571 }
572
573 /**
574 * \test Test the pruning of SACK ranges.
575 *
576 * \retval On success it returns 1 and on failure 0.
577 */
578
StreamTcpSackTest08(void)579 static int StreamTcpSackTest08 (void)
580 {
581 TcpStream stream;
582 memset(&stream, 0, sizeof(stream));
583 stream.window = 100;
584
585 StreamTcpSackInsertRange(&stream, 0, 9);
586 StreamTcpSackInsertRange(&stream, 11, 19);
587 StreamTcpSackInsertRange(&stream, 21, 29);
588 StreamTcpSackInsertRange(&stream, 31, 39);
589 StreamTcpSackInsertRange(&stream, 0, 40);
590 #ifdef DEBUG
591 StreamTcpSackPrintList(&stream);
592 #endif /* DEBUG */
593
594 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
595 FAIL_IF_NULL(rec);
596 FAIL_IF(rec->le != 0);
597 FAIL_IF(rec->re != 40);
598 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
599
600 stream.last_ack = 41;
601 StreamTcpSackPruneList(&stream);
602 FAIL_IF(StreamTcpSackedSize(&stream) != 0);
603
604 StreamTcpSackFreeList(&stream);
605 PASS;
606 }
607
608 /**
609 * \test Test the pruning of SACK ranges.
610 *
611 * \retval On success it returns 1 and on failure 0.
612 */
613
StreamTcpSackTest09(void)614 static int StreamTcpSackTest09 (void)
615 {
616 TcpStream stream;
617 memset(&stream, 0, sizeof(stream));
618 stream.window = 100;
619
620 StreamTcpSackInsertRange(&stream, 0, 9);
621 StreamTcpSackInsertRange(&stream, 11, 19);
622 StreamTcpSackInsertRange(&stream, 21, 29);
623 StreamTcpSackInsertRange(&stream, 31, 39);
624 StreamTcpSackInsertRange(&stream, 0, 40);
625
626 #ifdef DEBUG
627 StreamTcpSackPrintList(&stream);
628 #endif /* DEBUG */
629
630 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
631 FAIL_IF_NULL(rec);
632 FAIL_IF(rec->le != 0);
633 FAIL_IF(rec->re != 40);
634 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
635
636 stream.last_ack = 39;
637 StreamTcpSackPruneList(&stream);
638 FAIL_IF(StreamTcpSackedSize(&stream) != 1);
639
640 StreamTcpSackFreeList(&stream);
641 PASS;
642 }
643
644 /**
645 * \test Test the pruning of SACK ranges.
646 *
647 * \retval On success it returns 1 and on failure 0.
648 */
649
StreamTcpSackTest10(void)650 static int StreamTcpSackTest10 (void)
651 {
652 TcpStream stream;
653 memset(&stream, 0, sizeof(stream));
654 stream.window = 1000;
655
656 StreamTcpSackInsertRange(&stream, 100, 119);
657 StreamTcpSackInsertRange(&stream, 111, 119);
658 StreamTcpSackInsertRange(&stream, 121, 129);
659 StreamTcpSackInsertRange(&stream, 131, 139);
660 StreamTcpSackInsertRange(&stream, 100, 140);
661 #ifdef DEBUG
662 StreamTcpSackPrintList(&stream);
663 #endif /* DEBUG */
664
665 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
666 FAIL_IF_NULL(rec);
667 FAIL_IF(rec->le != 100);
668 FAIL_IF(rec->re != 140);
669 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
670
671 stream.last_ack = 99;
672 StreamTcpSackPruneList(&stream);
673 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
674
675 StreamTcpSackFreeList(&stream);
676 PASS;
677 }
678
679 /**
680 * \test Test the pruning of SACK ranges.
681 *
682 * \retval On success it returns 1 and on failure 0.
683 */
684
StreamTcpSackTest11(void)685 static int StreamTcpSackTest11 (void)
686 {
687 TcpStream stream;
688 memset(&stream, 0, sizeof(stream));
689 stream.window = 1000;
690
691 StreamTcpSackInsertRange(&stream, 100, 119);
692 StreamTcpSackInsertRange(&stream, 111, 119);
693 StreamTcpSackInsertRange(&stream, 121, 129);
694 StreamTcpSackInsertRange(&stream, 131, 139);
695 StreamTcpSackInsertRange(&stream, 101, 140);
696 #ifdef DEBUG
697 StreamTcpSackPrintList(&stream);
698 #endif /* DEBUG */
699
700 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
701 FAIL_IF_NULL(rec);
702 FAIL_IF(rec->le != 100);
703 FAIL_IF(rec->re != 140);
704 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
705
706 stream.last_ack = 99;
707 StreamTcpSackPruneList(&stream);
708 FAIL_IF(StreamTcpSackedSize(&stream) != 40);
709
710 StreamTcpSackFreeList(&stream);
711 PASS;
712 }
713
714 /**
715 * \test Test the pruning of SACK ranges.
716 *
717 * \retval On success it returns 1 and on failure 0.
718 */
719
StreamTcpSackTest12(void)720 static int StreamTcpSackTest12 (void)
721 {
722 TcpStream stream;
723 memset(&stream, 0, sizeof(stream));
724 stream.window = 2000;
725
726 StreamTcpSackInsertRange(&stream, 800, 1000);
727 StreamTcpSackInsertRange(&stream, 700, 900);
728 StreamTcpSackInsertRange(&stream, 600, 800);
729 StreamTcpSackInsertRange(&stream, 500, 700);
730 StreamTcpSackInsertRange(&stream, 100, 600);
731 #ifdef DEBUG
732 StreamTcpSackPrintList(&stream);
733 #endif /* DEBUG */
734
735 StreamTcpSackRecord *rec = RB_MIN(TCPSACK, &stream.sack_tree);
736 FAIL_IF_NULL(rec);
737 FAIL_IF(rec->le != 100);
738 FAIL_IF(rec->re != 1000);
739 FAIL_IF(StreamTcpSackedSize(&stream) != 900);
740
741 StreamTcpSackInsertRange(&stream, 0, 1000);
742 FAIL_IF(StreamTcpSackedSize(&stream) != 1000);
743
744 stream.last_ack = 500;
745 StreamTcpSackPruneList(&stream);
746 FAIL_IF(StreamTcpSackedSize(&stream) != 500);
747
748 StreamTcpSackFreeList(&stream);
749 PASS;
750 }
751
752 /**
753 * \test Test the insertion on out of window condition.
754 *
755 * \retval On success it returns 1 and on failure 0.
756 */
757
StreamTcpSackTest13(void)758 static int StreamTcpSackTest13 (void) {
759 TcpStream stream;
760 memset(&stream, 0, sizeof(stream));
761 stream.last_ack = 10000;
762 stream.window = 2000;
763
764 for (int i = 0; i < 10; i++) {
765 StreamTcpSackInsertRange(&stream, 100+(20*i), 110+(20*i));
766 }
767 #ifdef DEBUG
768 StreamTcpSackPrintList(&stream);
769 #endif /* DEBUG */
770
771 FAIL_IF(StreamTcpSackedSize(&stream) != 0);
772
773 StreamTcpSackFreeList(&stream);
774 PASS;
775 }
776
777 /**
778 * \test Test the insertion of out of window condition.
779 *
780 * \retval On success it returns 1 and on failure 0.
781 */
782
StreamTcpSackTest14(void)783 static int StreamTcpSackTest14 (void) {
784 TcpStream stream;
785 memset(&stream, 0, sizeof(stream));
786 stream.last_ack = 1000;
787 stream.window = 2000;
788
789 for (int i = 0; i < 10; i++) {
790 StreamTcpSackInsertRange(&stream, 4000+(20*i), 4010+(20*i));
791 }
792 #ifdef DEBUG
793 StreamTcpSackPrintList(&stream);
794 #endif /* DEBUG */
795
796 FAIL_IF(StreamTcpSackedSize(&stream) != 0);
797
798 StreamTcpSackFreeList(&stream);
799 PASS;
800 }
801
802 #endif /* UNITTESTS */
803
StreamTcpSackRegisterTests(void)804 void StreamTcpSackRegisterTests (void)
805 {
806 #ifdef UNITTESTS
807 UtRegisterTest("StreamTcpSackTest01 -- Insertion", StreamTcpSackTest01);
808 UtRegisterTest("StreamTcpSackTest02 -- Insertion", StreamTcpSackTest02);
809 UtRegisterTest("StreamTcpSackTest03 -- Insertion", StreamTcpSackTest03);
810 UtRegisterTest("StreamTcpSackTest04 -- Insertion", StreamTcpSackTest04);
811 UtRegisterTest("StreamTcpSackTest05 -- Insertion", StreamTcpSackTest05);
812 UtRegisterTest("StreamTcpSackTest06 -- Insertion", StreamTcpSackTest06);
813 UtRegisterTest("StreamTcpSackTest07 -- Pruning", StreamTcpSackTest07);
814 UtRegisterTest("StreamTcpSackTest08 -- Pruning", StreamTcpSackTest08);
815 UtRegisterTest("StreamTcpSackTest09 -- Pruning", StreamTcpSackTest09);
816 UtRegisterTest("StreamTcpSackTest10 -- Pruning", StreamTcpSackTest10);
817 UtRegisterTest("StreamTcpSackTest11 -- Insertion && Pruning",
818 StreamTcpSackTest11);
819 UtRegisterTest("StreamTcpSackTest12 -- Insertion && Pruning",
820 StreamTcpSackTest12);
821 UtRegisterTest("StreamTcpSackTest13 -- Insertion out of window",
822 StreamTcpSackTest13);
823 UtRegisterTest("StreamTcpSackTest14 -- Insertion out of window",
824 StreamTcpSackTest14);
825 #endif
826 }
827