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