suricata
stream-tcp-sack.c
Go to the documentation of this file.
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 
33 
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
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 
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 
73 static inline void StreamTcpSackRecordFree(StreamTcpSackRecord *rec)
74 {
75  SCFree(rec);
76  StreamTcpDecrMemuse((uint64_t)sizeof(*rec));
77 }
78 
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 
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 
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  */
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  */
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 
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  */
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 }
#define SCLogDebug(...)
Definition: util-debug.h:335
void StreamTcpSackRegisterTests(void)
struct HtpBodyChunk_ * next
#define RB_FOREACH(x, name, head)
Definition: tree.h:783
int StreamTcpCheckMemcap(uint64_t size)
Check if alloc&#39;ing "size" would mean we&#39;re over memcap.
Definition: stream-tcp.c:167
#define PASS
Pass the test.
#define unlikely(expr)
Definition: util-optimize.h:35
#define RB_FOREACH_FROM(x, name, y)
Definition: tree.h:788
#define FAIL_IF(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:71
int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p)
Update stream with SACK records from a TCP packet.
void StreamTcpDecrMemuse(uint64_t size)
Definition: stream-tcp.c:134
RB_GENERATE(TCPSACK, StreamTcpSackRecord, rb, TcpSackCompare)
#define SEQ_GEQ(a, b)
void StreamTcpSackFreeList(TcpStream *stream)
Free SACK tree from a stream.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
void StreamTcpSackPruneList(TcpStream *stream)
#define SCEnter(...)
Definition: util-debug.h:337
#define SEQ_GT(a, b)
#define SCReturnInt(x)
Definition: util-debug.h:341
#define SCNtohl(x)
#define SEQ_LEQ(a, b)
#define RB_FOREACH_REVERSE_FROM(x, name, y)
Definition: tree.h:803
#define RB_MIN(name, x)
Definition: tree.h:780
#define TCP_GET_SACK_CNT(p)
Definition: decode-tcp.h:105
#define TCP_GET_SACK_PTR(p)
Definition: decode-tcp.h:104
#define SCMalloc(a)
Definition: util-mem.h:222
#define SCFree(a)
Definition: util-mem.h:322
PoolThreadReserved res
uint32_t sack_size
#define RB_FOREACH_SAFE(x, name, head, y)
Definition: tree.h:793
void StreamTcpIncrMemuse(uint64_t size)
Definition: stream-tcp.c:127
struct TCPSACK sack_tree
int TcpSackCompare(struct StreamTcpSackRecord *a, struct StreamTcpSackRecord *b)
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
#define SEQ_EQ(a, b)
#define SCReturn
Definition: util-debug.h:339
#define SEQ_LT(a, b)
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
Definition: util-unittest.h:82