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