Implement sack based tcp loss recovery (RFC 6675)
[vpp.git] / src / vnet / tcp / tcp_test.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vnet/tcp/tcp.h>
16
17 #define TCP_TEST_I(_cond, _comment, _args...)                   \
18 ({                                                              \
19   int _evald = (_cond);                                         \
20   if (!(_evald)) {                                              \
21     fformat(stderr, "FAIL:%d: " _comment "\n",                  \
22             __LINE__, ##_args);                                 \
23   } else {                                                      \
24     fformat(stderr, "PASS:%d: " _comment "\n",                  \
25             __LINE__, ##_args);                                 \
26   }                                                             \
27   _evald;                                                       \
28 })
29
30 #define TCP_TEST(_cond, _comment, _args...)                     \
31 {                                                               \
32     if (!TCP_TEST_I(_cond, _comment, ##_args)) {                \
33         return 1;                                               \
34     }                                                           \
35 }
36
37 static int
38 tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input)
39 {
40   tcp_connection_t _tc, *tc = &_tc;
41   sack_scoreboard_t *sb = &tc->sack_sb;
42   sack_block_t *sacks = 0, block;
43   sack_scoreboard_hole_t *hole;
44   int i, verbose = 0;
45
46   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
47     {
48       if (unformat (input, "verbose"))
49         verbose = 1;
50     }
51
52   memset (tc, 0, sizeof (*tc));
53
54   tc->snd_una = 0;
55   tc->snd_una_max = 1000;
56   tc->snd_nxt = 1000;
57   tc->rcv_opts.flags |= TCP_OPTS_FLAG_SACK;
58   scoreboard_init (&tc->sack_sb);
59
60   for (i = 0; i < 1000 / 100; i++)
61     {
62       block.start = i * 100;
63       block.end = (i + 1) * 100;
64       vec_add1 (sacks, block);
65     }
66
67   /*
68    * Inject even blocks
69    */
70
71   for (i = 0; i < 1000 / 200; i++)
72     {
73       vec_add1 (tc->rcv_opts.sacks, sacks[i * 2]);
74     }
75   tc->rcv_opts.n_sack_blocks = vec_len (tc->rcv_opts.sacks);
76   tcp_rcv_sacks (tc, 0);
77
78   if (verbose)
79     vlib_cli_output (vm, "sb after even blocks:\n%U", format_tcp_scoreboard,
80                      sb);
81
82   TCP_TEST ((pool_elts (sb->holes) == 5),
83             "scoreboard has %d elements", pool_elts (sb->holes));
84
85   /* First SACK block should be rejected */
86   hole = scoreboard_first_hole (sb);
87   TCP_TEST ((hole->start == 0 && hole->end == 200),
88             "first hole start %u end %u", hole->start, hole->end);
89   hole = scoreboard_last_hole (sb);
90   TCP_TEST ((hole->start == 900 && hole->end == 1000),
91             "last hole start %u end %u", hole->start, hole->end);
92   TCP_TEST ((sb->sacked_bytes == 400), "sacked bytes %d", sb->sacked_bytes);
93   TCP_TEST ((sb->snd_una_adv == 0), "snd_una_adv %u", sb->snd_una_adv);
94   TCP_TEST ((sb->last_sacked_bytes == 400),
95             "last sacked bytes %d", sb->last_sacked_bytes);
96   TCP_TEST ((sb->high_sacked == 900), "max byte sacked %u", sb->high_sacked);
97   /*
98    * Inject odd blocks
99    */
100
101   vec_reset_length (tc->rcv_opts.sacks);
102   for (i = 0; i < 1000 / 200; i++)
103     {
104       vec_add1 (tc->rcv_opts.sacks, sacks[i * 2 + 1]);
105     }
106   tc->rcv_opts.n_sack_blocks = vec_len (tc->rcv_opts.sacks);
107   tcp_rcv_sacks (tc, 0);
108
109   if (verbose)
110     vlib_cli_output (vm, "sb after odd blocks:\n%U", format_tcp_scoreboard,
111                      sb);
112
113   hole = scoreboard_first_hole (sb);
114   TCP_TEST ((pool_elts (sb->holes) == 1),
115             "scoreboard has %d holes", pool_elts (sb->holes));
116   TCP_TEST ((hole->start == 0 && hole->end == 100),
117             "first hole start %u end %u", hole->start, hole->end);
118   TCP_TEST ((sb->sacked_bytes == 900), "sacked bytes %d", sb->sacked_bytes);
119   TCP_TEST ((sb->snd_una_adv == 0), "snd_una_adv %u", sb->snd_una_adv);
120   TCP_TEST ((sb->high_sacked == 1000), "max sacked byte %u", sb->high_sacked);
121   TCP_TEST ((sb->last_sacked_bytes == 500),
122             "last sacked bytes %d", sb->last_sacked_bytes);
123
124   /*
125    *  Ack until byte 100, all bytes are now acked + sacked
126    */
127   tcp_rcv_sacks (tc, 100);
128   if (verbose)
129     vlib_cli_output (vm, "ack until byte 100:\n%U", format_tcp_scoreboard,
130                      sb);
131
132   TCP_TEST ((pool_elts (sb->holes) == 0),
133             "scoreboard has %d elements", pool_elts (sb->holes));
134   TCP_TEST ((sb->snd_una_adv == 900),
135             "snd_una_adv after ack %u", sb->snd_una_adv);
136   TCP_TEST ((sb->high_sacked == 1000), "max sacked byte %u", sb->high_sacked);
137   TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes);
138   TCP_TEST ((sb->last_sacked_bytes == 0),
139             "last sacked bytes %d", sb->last_sacked_bytes);
140
141   /*
142    * Add new block
143    */
144
145   vec_reset_length (tc->rcv_opts.sacks);
146
147   block.start = 1200;
148   block.end = 1300;
149   vec_add1 (tc->rcv_opts.sacks, block);
150
151   if (verbose)
152     vlib_cli_output (vm, "add [1200, 1300]:\n%U", format_tcp_scoreboard, sb);
153   tc->snd_una_max = 1500;
154   tc->snd_una = 1000;
155   tc->snd_nxt = 1500;
156   tcp_rcv_sacks (tc, 1000);
157
158   if (verbose)
159     vlib_cli_output (vm, "sb snd_una_max 1500, snd_una 1000:\n%U",
160                      format_tcp_scoreboard, sb);
161
162   TCP_TEST ((sb->snd_una_adv == 0),
163             "snd_una_adv after ack %u", sb->snd_una_adv);
164   TCP_TEST ((pool_elts (sb->holes) == 2),
165             "scoreboard has %d holes", pool_elts (sb->holes));
166   hole = scoreboard_first_hole (sb);
167   TCP_TEST ((hole->start == 1000 && hole->end == 1200),
168             "first hole start %u end %u", hole->start, hole->end);
169   TCP_TEST ((sb->snd_una_adv == 0),
170             "snd_una_adv after ack %u", sb->snd_una_adv);
171   TCP_TEST ((sb->high_sacked == 1300), "max sacked byte %u", sb->high_sacked);
172   hole = scoreboard_last_hole (sb);
173   TCP_TEST ((hole->start == 1300 && hole->end == 1500),
174             "last hole start %u end %u", hole->start, hole->end);
175   TCP_TEST ((sb->sacked_bytes == 100), "sacked bytes %d", sb->sacked_bytes);
176
177   /*
178    * Ack first hole
179    */
180
181   vec_reset_length (tc->rcv_opts.sacks);
182   tcp_rcv_sacks (tc, 1200);
183
184   if (verbose)
185     vlib_cli_output (vm, "sb ack up to byte 1200:\n%U", format_tcp_scoreboard,
186                      sb);
187
188   TCP_TEST ((sb->snd_una_adv == 100),
189             "snd_una_adv after ack %u", sb->snd_una_adv);
190   TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes);
191   TCP_TEST ((pool_elts (sb->holes) == 1),
192             "scoreboard has %d elements", pool_elts (sb->holes));
193
194   /*
195    * Add some more blocks and then remove all
196    */
197   vec_reset_length (tc->rcv_opts.sacks);
198   for (i = 0; i < 5; i++)
199     {
200       block.start = i * 100 + 1200;
201       block.end = (i + 1) * 100 + 1200;
202       vec_add1 (tc->rcv_opts.sacks, block);
203     }
204   tcp_rcv_sacks (tc, 1900);
205
206   scoreboard_clear (sb);
207   if (verbose)
208     vlib_cli_output (vm, "sb cleared all:\n%U", format_tcp_scoreboard, sb);
209
210   TCP_TEST ((pool_elts (sb->holes) == 0),
211             "number of holes %d", pool_elts (sb->holes));
212   TCP_TEST ((sb->head == TCP_INVALID_SACK_HOLE_INDEX), "head %u", sb->head);
213   TCP_TEST ((sb->tail == TCP_INVALID_SACK_HOLE_INDEX), "tail %u", sb->tail);
214
215   /*
216    * Re-inject odd blocks and ack them all
217    */
218
219   tc->snd_una = 0;
220   tc->snd_una_max = 1000;
221   tc->snd_nxt = 1000;
222   for (i = 0; i < 5; i++)
223     {
224       vec_add1 (tc->rcv_opts.sacks, sacks[i * 2 + 1]);
225     }
226   tc->rcv_opts.n_sack_blocks = vec_len (tc->rcv_opts.sacks);
227   tcp_rcv_sacks (tc, 0);
228   if (verbose)
229     vlib_cli_output (vm, "sb added odd blocks and ack [0, 950]:\n%U",
230                      format_tcp_scoreboard, sb);
231
232   tcp_rcv_sacks (tc, 950);
233
234   if (verbose)
235     vlib_cli_output (vm, "sb added odd blocks and ack [0, 950]:\n%U",
236                      format_tcp_scoreboard, sb);
237
238   TCP_TEST ((pool_elts (sb->holes) == 0),
239             "scoreboard has %d elements", pool_elts (sb->holes));
240   TCP_TEST ((sb->snd_una_adv == 50), "snd_una_adv %u", sb->snd_una_adv);
241   TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes);
242   TCP_TEST ((sb->last_sacked_bytes == 0),
243             "last sacked bytes %d", sb->last_sacked_bytes);
244
245   return 0;
246 }
247
248 static int
249 tcp_test_sack_tx (vlib_main_t * vm, unformat_input_t * input)
250 {
251   tcp_connection_t _tc, *tc = &_tc;
252   sack_block_t *sacks;
253   int i, verbose = 0;
254
255   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
256     {
257       if (unformat (input, "verbose"))
258         verbose = 1;
259       else
260         {
261           vlib_cli_output (vm, "parse error: '%U'", format_unformat_error,
262                            input);
263           return -1;
264         }
265     }
266
267   memset (tc, 0, sizeof (*tc));
268
269   /*
270    * Add odd sack block pairs
271    */
272   for (i = 1; i < 10; i += 2)
273     {
274       tcp_update_sack_list (tc, i * 100, (i + 1) * 100);
275     }
276
277   TCP_TEST ((vec_len (tc->snd_sacks) == 5), "sack blocks %d expected %d",
278             vec_len (tc->snd_sacks), 5);
279   TCP_TEST ((tc->snd_sacks[0].start = 900),
280             "first sack block start %u expected %u", tc->snd_sacks[0].start,
281             900);
282
283   /*
284    * Try to add one extra
285    */
286   sacks = vec_dup (tc->snd_sacks);
287
288   tcp_update_sack_list (tc, 1100, 1200);
289   TCP_TEST ((vec_len (tc->snd_sacks) == 5), "sack blocks %d expected %d",
290             vec_len (tc->snd_sacks), 5);
291   TCP_TEST ((tc->snd_sacks[0].start == 1100),
292             "first sack block start %u expected %u", tc->snd_sacks[0].start,
293             1100);
294
295   /* restore */
296   vec_free (tc->snd_sacks);
297   tc->snd_sacks = sacks;
298
299   /*
300    * Overlap first 2 segment
301    */
302   tc->rcv_nxt = 300;
303   tcp_update_sack_list (tc, 300, 300);
304   if (verbose)
305     vlib_cli_output (vm, "overlap first 2 segments:\n%U",
306                      format_tcp_sacks, tc);
307   TCP_TEST ((vec_len (tc->snd_sacks) == 3), "sack blocks %d expected %d",
308             vec_len (tc->snd_sacks), 3);
309   TCP_TEST ((tc->snd_sacks[0].start == 900),
310             "first sack block start %u expected %u", tc->snd_sacks[0].start,
311             500);
312
313   /*
314    * Add a new segment
315    */
316   tcp_update_sack_list (tc, 1100, 1200);
317   if (verbose)
318     vlib_cli_output (vm, "add new segment [1100, 1200]\n%U",
319                      format_tcp_sacks, tc);
320   TCP_TEST ((vec_len (tc->snd_sacks) == 4), "sack blocks %d expected %d",
321             vec_len (tc->snd_sacks), 4);
322   TCP_TEST ((tc->snd_sacks[0].start == 1100),
323             "first sack block start %u expected %u", tc->snd_sacks[0].start,
324             1100);
325
326   /*
327    * Join middle segments
328    */
329   tcp_update_sack_list (tc, 800, 900);
330   if (verbose)
331     vlib_cli_output (vm, "join middle segments [800, 900]\n%U",
332                      format_tcp_sacks, tc);
333
334   TCP_TEST ((vec_len (tc->snd_sacks) == 3), "sack blocks %d expected %d",
335             vec_len (tc->snd_sacks), 3);
336   TCP_TEST ((tc->snd_sacks[0].start == 700),
337             "first sack block start %u expected %u", tc->snd_sacks[0].start,
338             1100);
339
340   /*
341    * Advance rcv_nxt to overlap all
342    */
343   tc->rcv_nxt = 1200;
344   tcp_update_sack_list (tc, 1200, 1200);
345   if (verbose)
346     vlib_cli_output (vm, "advance rcv_nxt to 1200\n%U", format_tcp_sacks, tc);
347   TCP_TEST ((vec_len (tc->snd_sacks) == 0), "sack blocks %d expected %d",
348             vec_len (tc->snd_sacks), 0);
349   return 0;
350 }
351
352 static int
353 tcp_test_sack (vlib_main_t * vm, unformat_input_t * input)
354 {
355   int res = 0;
356
357   /* Run all tests */
358   if (unformat_check_input (input) == UNFORMAT_END_OF_INPUT)
359     {
360       if (tcp_test_sack_tx (vm, input))
361         {
362           return -1;
363         }
364
365       if (tcp_test_sack_rx (vm, input))
366         {
367           return -1;
368         }
369     }
370   else
371     {
372       if (unformat (input, "tx"))
373         {
374           res = tcp_test_sack_tx (vm, input);
375         }
376       else if (unformat (input, "rx"))
377         {
378           res = tcp_test_sack_rx (vm, input);
379         }
380     }
381
382   return res;
383 }
384
385
386 typedef struct
387 {
388   u32 offset;
389   u32 len;
390 } test_pattern_t;
391
392 /* *INDENT-OFF* */
393 test_pattern_t test_pattern[] = {
394   {380, 8}, {768, 8}, {1156, 8}, {1544, 8}, {1932, 8}, {2320, 8}, {2708, 8},
395   {2992, 8}, {372, 8}, {760, 8}, {1148, 8}, {1536, 8}, {1924, 8}, {2312, 8},
396   {2700, 8}, {2984, 8}, {364, 8}, {752, 8}, {1140, 8}, {1528, 8}, {1916, 8},
397   {2304, 8}, {2692, 8}, {2976, 8}, {356, 8}, {744, 8}, {1132, 8}, {1520, 8},
398   {1908, 8}, {2296, 8}, {2684, 8}, {2968, 8}, {348, 8}, {736, 8}, {1124, 8},
399   {1512, 8}, {1900, 8}, {2288, 8}, {2676, 8}, {2960, 8}, {340, 8}, {728, 8},
400   {1116, 8}, {1504, 8}, {1892, 8}, {2280, 8}, {2668, 8}, {2952, 8}, {332, 8},
401   {720, 8}, {1108, 8}, {1496, 8}, {1884, 8}, {2272, 8}, {2660, 8}, {2944, 8},
402   {324, 8}, {712, 8}, {1100, 8}, {1488, 8}, {1876, 8}, {2264, 8}, {2652, 8},
403   {2936, 8}, {316, 8}, {704, 8}, {1092, 8}, {1480, 8}, {1868, 8}, {2256, 8},
404   {2644, 8}, {2928, 8}, {308, 8}, {696, 8}, {1084, 8}, {1472, 8}, {1860, 8},
405   {2248, 8}, {2636, 8}, {2920, 8}, {300, 8}, {688, 8}, {1076, 8}, {1464, 8},
406   {1852, 8}, {2240, 8}, {2628, 8}, {2912, 8}, {292, 8}, {680, 8}, {1068, 8},
407   {1456, 8}, {1844, 8}, {2232, 8}, {2620, 8}, {2904, 8}, {284, 8}, {672, 8},
408   {1060, 8}, {1448, 8}, {1836, 8}, {2224, 8}, {2612, 8}, {2896, 8}, {276, 8},
409   {664, 8}, {1052, 8}, {1440, 8}, {1828, 8},  {2216, 8}, {2604, 8}, {2888, 8},
410   {268, 8}, {656, 8}, {1044, 8}, {1432, 8}, {1820, 8}, {2208, 8}, {2596, 8},
411   {2880, 8}, {260, 8}, {648, 8}, {1036, 8}, {1424, 8}, {1812, 8}, {2200, 8},
412   {2588, 8}, {2872, 8}, {252, 8}, {640, 8}, {1028, 8}, {1416, 8}, {1804, 8},
413   {2192, 8}, {2580, 8}, {2864, 8}, {244, 8}, {632, 8}, {1020, 8}, {1408, 8},
414   {1796, 8}, {2184, 8}, {2572, 8}, {2856, 8}, {236, 8}, {624, 8}, {1012, 8},
415   {1400, 8}, {1788, 8}, {2176, 8}, {2564, 8}, {2848, 8}, {228, 8}, {616, 8},
416   {1004, 8}, {1392, 8}, {1780, 8}, {2168, 8}, {2556, 8}, {2840, 8}, {220, 8},
417   {608, 8}, {996, 8}, {1384, 8}, {1772, 8}, {2160, 8}, {2548, 8}, {2832, 8},
418   {212, 8}, {600, 8}, {988, 8}, {1376, 8}, {1764, 8}, {2152, 8}, {2540, 8},
419   {2824, 8}, {204, 8}, {592, 8}, {980, 8}, {1368, 8}, {1756, 8}, {2144, 8},
420   {2532, 8}, {2816, 8}, {196, 8}, {584, 8}, {972, 8}, {1360, 8}, {1748, 8},
421   {2136, 8}, {2524, 8}, {2808, 8}, {188, 8}, {576, 8}, {964, 8}, {1352, 8},
422   {1740, 8}, {2128, 8}, {2516, 8}, {2800, 8}, {180, 8}, {568, 8}, {956, 8},
423   {1344, 8}, {1732, 8}, {2120, 8}, {2508, 8}, {2792, 8}, {172, 8}, {560, 8},
424   {948, 8}, {1336, 8}, {1724, 8}, {2112, 8}, {2500, 8}, {2784, 8}, {164, 8},
425   {552, 8}, {940, 8}, {1328, 8}, {1716, 8}, {2104, 8}, {2492, 8}, {2776, 8},
426   {156, 8}, {544, 8}, {932, 8}, {1320, 8}, {1708, 8}, {2096, 8}, {2484, 8},
427   {2768, 8}, {148, 8}, {536, 8}, {924, 8}, {1312, 8}, {1700, 8}, {2088, 8},
428   {2476, 8}, {2760, 8}, {140, 8}, {528, 8}, {916, 8}, {1304, 8}, {1692, 8},
429   {2080, 8}, {2468, 8}, {2752, 8}, {132, 8}, {520, 8}, {908, 8}, {1296, 8},
430   {1684, 8}, {2072, 8}, {2460, 8}, {2744, 8}, {124, 8}, {512, 8}, {900, 8},
431   {1288, 8}, {1676, 8}, {2064, 8}, {2452, 8}, {2736, 8}, {116, 8}, {504, 8},
432   {892, 8}, {1280, 8}, {1668, 8}, {2056, 8}, {2444, 8}, {2728, 8}, {108, 8},
433   {496, 8}, {884, 8}, {1272, 8}, {1660, 8}, {2048, 8}, {2436, 8}, {2720, 8},
434   {100, 8}, {488, 8}, {876, 8}, {1264, 8}, {1652, 8}, {2040, 8}, {2428, 8},
435   {2716, 4}, {92, 8}, {480, 8}, {868, 8}, {1256, 8}, {1644, 8}, {2032, 8},
436   {2420, 8}, {84, 8}, {472, 8}, {860, 8}, {1248, 8}, {1636, 8}, {2024, 8},
437   {2412, 8}, {76, 8}, {464, 8}, {852, 8}, {1240, 8}, {1628, 8}, {2016, 8},
438   {2404, 8}, {68, 8}, {456, 8}, {844, 8}, {1232, 8}, {1620, 8}, {2008, 8},
439   {2396, 8}, {60, 8}, {448, 8}, {836, 8}, {1224, 8}, {1612, 8}, {2000, 8},
440   {2388, 8}, {52, 8}, {440, 8}, {828, 8}, {1216, 8}, {1604, 8}, {1992, 8},
441   {2380, 8}, {44, 8}, {432, 8}, {820, 8}, {1208, 8}, {1596, 8}, {1984, 8},
442   {2372, 8}, {36, 8}, {424, 8}, {812, 8}, {1200, 8}, {1588, 8}, {1976, 8},
443   {2364, 8}, {28, 8}, {416, 8}, {804, 8}, {1192, 8}, {1580, 8}, {1968, 8},
444   {2356, 8}, {20, 8}, {408, 8}, {796, 8}, {1184, 8}, {1572, 8}, {1960, 8},
445   {2348, 8}, {12, 8}, {400, 8}, {788, 8}, {1176, 8}, {1564, 8}, {1952, 8},
446   {2340, 8}, {4, 8}, {392, 8}, {780, 8}, {1168, 8}, {1556, 8}, {1944, 8},
447   {2332, 8},
448   /* missing from original data set */
449   {388, 4}, {776, 4}, {1164, 4}, {1552, 4}, {1940, 4}, {2328, 4},
450 };
451 /* *INDENT-ON* */
452
453 int
454 pattern_cmp (const void *arg1, const void *arg2)
455 {
456   test_pattern_t *a1 = (test_pattern_t *) arg1;
457   test_pattern_t *a2 = (test_pattern_t *) arg2;
458
459   if (a1->offset < a2->offset)
460     return -1;
461   else if (a1->offset > a2->offset)
462     return 1;
463   return 0;
464 }
465
466 static u8
467 fifo_validate_pattern (vlib_main_t * vm, test_pattern_t * pattern,
468                        u32 pattern_length)
469 {
470   test_pattern_t *tp = pattern;
471   int i;
472
473   /* Go through the pattern and make 100% sure it's sane */
474   for (i = 0; i < pattern_length - 1; i++)
475     {
476       if (tp->offset + tp->len != (tp + 1)->offset)
477         {
478           vlib_cli_output (vm, "[%d] missing {%d, %d}", i,
479                            (tp->offset + tp->len),
480                            (tp + 1)->offset - (tp->offset + tp->len));
481           return 0;
482         }
483       tp++;
484     }
485   return 1;
486 }
487
488 static test_pattern_t *
489 fifo_get_validate_pattern (vlib_main_t * vm, test_pattern_t * test_data,
490                            u32 test_data_len)
491 {
492   test_pattern_t *validate_pattern = 0;
493
494   /* Validate, and try segments in order... */
495   vec_validate (validate_pattern, test_data_len - 1);
496   memcpy (validate_pattern, test_data,
497           test_data_len * sizeof (test_pattern_t));
498   qsort ((u8 *) validate_pattern, test_data_len, sizeof (test_pattern_t),
499          pattern_cmp);
500
501   if (fifo_validate_pattern (vm, validate_pattern, test_data_len) == 0)
502     return 0;
503
504   return validate_pattern;
505 }
506
507 static svm_fifo_t *
508 fifo_prepare (u32 fifo_size)
509 {
510   svm_fifo_t *f;
511   f = svm_fifo_create (fifo_size);
512
513   /* Paint fifo data vector with -1's */
514   memset (f->data, 0xFF, fifo_size);
515
516   return f;
517 }
518
519 static int
520 compare_data (u8 * data1, u8 * data2, u32 start, u32 len, u32 * index)
521 {
522   int i;
523
524   for (i = start; i < len; i++)
525     {
526       if (data1[i] != data2[i])
527         {
528           *index = i;
529           return 1;
530         }
531     }
532   return 0;
533 }
534
535 int
536 tcp_test_fifo1 (vlib_main_t * vm, unformat_input_t * input)
537 {
538   svm_fifo_t *f;
539   u32 fifo_size = 1 << 20;
540   u32 *test_data = 0;
541   u32 offset;
542   int i, rv, verbose = 0;
543   u32 data_word, test_data_len, j;
544   ooo_segment_t *ooo_seg;
545   u8 *data, *s, *data_buf = 0;
546
547   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
548     {
549       if (unformat (input, "verbose"))
550         verbose = 1;
551     }
552
553   test_data_len = fifo_size / sizeof (u32);
554   vec_validate (test_data, test_data_len - 1);
555
556   for (i = 0; i < vec_len (test_data); i++)
557     test_data[i] = i;
558
559   f = fifo_prepare (fifo_size);
560
561   /*
562    * Enqueue an initial (un-dequeued) chunk
563    */
564   rv = svm_fifo_enqueue_nowait (f, sizeof (u32), (u8 *) test_data);
565   TCP_TEST ((rv == sizeof (u32)), "enqueued %d", rv);
566   TCP_TEST ((f->tail == 4), "fifo tail %u", f->tail);
567
568   /*
569    * Create 3 chunks in the future. The offsets are relative
570    * to the current fifo tail
571    */
572   for (i = 0; i < 3; i++)
573     {
574       offset = (2 * i + 1) * sizeof (u32);
575       data = (u8 *) (test_data + (2 * i + 1));
576       if (i == 0)
577         {
578           rv = svm_fifo_enqueue_nowait (f, sizeof (u32), data);
579           rv = rv > 0 ? 0 : rv;
580         }
581       else
582         rv = svm_fifo_enqueue_with_offset (f, offset, sizeof (u32), data);
583       if (verbose)
584         vlib_cli_output (vm, "add [%d] [%d, %d]", 2 * i + 1, offset,
585                          offset + sizeof (u32));
586       if (rv)
587         {
588           clib_warning ("enqueue returned %d", rv);
589           goto err;
590         }
591     }
592
593   if (verbose)
594     vlib_cli_output (vm, "fifo after odd segs: %U", format_svm_fifo, f, 1);
595
596   TCP_TEST ((f->tail == 8), "fifo tail %u", f->tail);
597   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 2),
598             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
599
600   /*
601    * Try adding a completely overlapped segment
602    */
603   offset = 3 * sizeof (u32);
604   data = (u8 *) (test_data + 3);
605   rv = svm_fifo_enqueue_with_offset (f, offset, sizeof (u32), data);
606   if (rv)
607     {
608       clib_warning ("enqueue returned %d", rv);
609       goto err;
610     }
611
612   if (verbose)
613     vlib_cli_output (vm, "fifo after overlap seg: %U", format_svm_fifo, f, 1);
614
615   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 2),
616             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
617
618   /*
619    * Make sure format functions are not buggy
620    */
621   s = format (0, "%U", format_svm_fifo, f, 2);
622   vec_free (s);
623
624   /*
625    * Paint some of missing data backwards
626    */
627   for (i = 3; i > 1; i--)
628     {
629       offset = (2 * i + 0) * sizeof (u32);
630       data = (u8 *) (test_data + (2 * i + 0));
631       rv = svm_fifo_enqueue_with_offset (f, offset, sizeof (u32), data);
632       if (verbose)
633         vlib_cli_output (vm, "add [%d] [%d, %d]", 2 * i, offset,
634                          offset + sizeof (u32));
635       if (rv)
636         {
637           clib_warning ("enqueue returned %d", rv);
638           goto err;
639         }
640     }
641
642   if (verbose)
643     vlib_cli_output (vm, "fifo before missing link: %U", format_svm_fifo, f,
644                      1);
645   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 1),
646             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
647   ooo_seg = svm_fifo_first_ooo_segment (f);
648   TCP_TEST ((ooo_seg->start == 12),
649             "first ooo seg position %u", ooo_seg->start);
650   TCP_TEST ((ooo_seg->length == 16),
651             "first ooo seg length %u", ooo_seg->length);
652
653   /*
654    * Enqueue the missing u32
655    */
656   rv = svm_fifo_enqueue_nowait (f, sizeof (u32), (u8 *) (test_data + 2));
657   if (verbose)
658     vlib_cli_output (vm, "fifo after missing link: %U", format_svm_fifo, f,
659                      1);
660   TCP_TEST ((rv == 20), "bytes to be enqueued %u", rv);
661   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 0),
662             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
663
664   /*
665    * Collect results
666    */
667   for (i = 0; i < 7; i++)
668     {
669       rv = svm_fifo_dequeue_nowait (f, sizeof (u32), (u8 *) & data_word);
670       if (rv != sizeof (u32))
671         {
672           clib_warning ("bytes dequeues %u", rv);
673           goto err;
674         }
675       if (data_word != test_data[i])
676         {
677           clib_warning ("recovered [%d] %d not %d", i, data_word,
678                         test_data[i]);
679           goto err;
680         }
681     }
682
683   /*
684    * Test segment overlaps: last ooo segment overlaps all
685    */
686   svm_fifo_free (f);
687   f = fifo_prepare (fifo_size);
688
689   for (i = 0; i < 4; i++)
690     {
691       offset = (2 * i + 1) * sizeof (u32);
692       data = (u8 *) (test_data + (2 * i + 1));
693       rv = svm_fifo_enqueue_with_offset (f, offset, sizeof (u32), data);
694       if (verbose)
695         vlib_cli_output (vm, "add [%d] [%d, %d]", 2 * i + 1, offset,
696                          offset + sizeof (u32));
697       if (rv)
698         {
699           clib_warning ("enqueue returned %d", rv);
700           goto err;
701         }
702     }
703
704   rv = svm_fifo_enqueue_with_offset (f, 8, 21, data);
705   TCP_TEST ((rv == 0), "ooo enqueued %u", rv);
706   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 1),
707             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
708
709   vec_validate (data_buf, vec_len (data));
710   svm_fifo_peek (f, 0, vec_len (data), data_buf);
711   if (compare_data (data_buf, data, 8, vec_len (data), &j))
712     {
713       TCP_TEST (0, "[%d] peeked %u expected %u", j, data_buf[j], data[j]);
714     }
715   vec_reset_length (data_buf);
716
717   /*
718    * Test segment overlaps: enqueue and overlap ooo segments
719    */
720   svm_fifo_free (f);
721   f = fifo_prepare (fifo_size);
722
723   for (i = 0; i < 4; i++)
724     {
725       offset = (2 * i + 1) * sizeof (u32);
726       data = (u8 *) (test_data + (2 * i + 1));
727       rv = svm_fifo_enqueue_with_offset (f, offset, sizeof (u32), data);
728       if (verbose)
729         vlib_cli_output (vm, "add [%d] [%d, %d]", 2 * i + 1, offset,
730                          offset + sizeof (u32));
731       if (rv)
732         {
733           clib_warning ("enqueue returned %d", rv);
734           goto err;
735         }
736     }
737
738   rv = svm_fifo_enqueue_nowait (f, 29, data);
739   TCP_TEST ((rv == 32), "ooo enqueued %u", rv);
740   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 0),
741             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
742
743   vec_validate (data_buf, vec_len (data));
744   svm_fifo_peek (f, 0, vec_len (data), data_buf);
745   if (compare_data (data_buf, data, 0, vec_len (data), &j))
746     {
747       TCP_TEST (0, "[%d] peeked %u expected %u", j, data_buf[j], data[j]);
748     }
749
750   /* Try to peek beyond the data */
751   rv = svm_fifo_peek (f, svm_fifo_max_dequeue (f), vec_len (data), data_buf);
752   TCP_TEST ((rv == 0), "peeked %u expected 0", rv);
753
754   vec_free (data_buf);
755   svm_fifo_free (f);
756   vec_free (test_data);
757
758   return 0;
759
760 err:
761   svm_fifo_free (f);
762   vec_free (test_data);
763   return -1;
764 }
765
766 static int
767 tcp_test_fifo2 (vlib_main_t * vm)
768 {
769   svm_fifo_t *f;
770   u32 fifo_size = 1 << 20;
771   int i, rv, test_data_len;
772   u64 data64;
773   test_pattern_t *tp, *vp, *test_data;
774   ooo_segment_t *ooo_seg;
775
776   test_data = test_pattern;
777   test_data_len = ARRAY_LEN (test_pattern);
778
779   vp = fifo_get_validate_pattern (vm, test_data, test_data_len);
780
781   /* Create a fifo */
782   f = fifo_prepare (fifo_size);
783
784   /*
785    * Try with sorted data
786    */
787   for (i = 0; i < test_data_len; i++)
788     {
789       tp = vp + i;
790       data64 = tp->offset;
791       svm_fifo_enqueue_with_offset (f, tp->offset, tp->len, (u8 *) & data64);
792     }
793
794   /* Expected result: one big fat chunk at offset 4 */
795   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 1),
796             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
797   ooo_seg = svm_fifo_first_ooo_segment (f);
798   TCP_TEST ((ooo_seg->start == 4),
799             "first ooo seg position %u", ooo_seg->start);
800   TCP_TEST ((ooo_seg->length == 2996),
801             "first ooo seg length %u", ooo_seg->length);
802
803   data64 = 0;
804   rv = svm_fifo_enqueue_nowait (f, sizeof (u32), (u8 *) & data64);
805   TCP_TEST ((rv == 3000), "bytes to be enqueued %u", rv);
806
807   svm_fifo_free (f);
808   vec_free (vp);
809
810   /*
811    * Now try it again w/ unsorted data...
812    */
813
814   f = fifo_prepare (fifo_size);
815
816   for (i = 0; i < test_data_len; i++)
817     {
818       tp = &test_data[i];
819       data64 = tp->offset;
820       rv = svm_fifo_enqueue_with_offset (f, tp->offset, tp->len,
821                                          (u8 *) & data64);
822       if (rv)
823         {
824           clib_warning ("enqueue returned %d", rv);
825         }
826     }
827
828   /* Expecting the same result: one big fat chunk at offset 4 */
829   TCP_TEST ((svm_fifo_number_ooo_segments (f) == 1),
830             "number of ooo segments %u", svm_fifo_number_ooo_segments (f));
831   ooo_seg = svm_fifo_first_ooo_segment (f);
832   TCP_TEST ((ooo_seg->start == 4),
833             "first ooo seg position %u", ooo_seg->start);
834   TCP_TEST ((ooo_seg->length == 2996),
835             "first ooo seg length %u", ooo_seg->length);
836
837   data64 = 0;
838   rv = svm_fifo_enqueue_nowait (f, sizeof (u32), (u8 *) & data64);
839
840   TCP_TEST ((rv == 3000), "bytes to be enqueued %u", rv);
841
842   svm_fifo_free (f);
843
844   return 0;
845 }
846
847 static int
848 tcp_test_fifo3 (vlib_main_t * vm, unformat_input_t * input)
849 {
850   svm_fifo_t *f;
851   u32 fifo_size = 4 << 10;
852   u32 fifo_initial_offset = 0;
853   u32 total_size = 2 << 10;
854   int overlap = 0, verbose = 0, randomize = 1, drop = 0, in_seq_all = 0;
855   u8 *data_pattern = 0, *data_buf = 0;
856   test_pattern_t *tp, *generate = 0;
857   u32 nsegs = 2, seg_size, length_so_far;
858   u32 current_offset, offset_increment, len_this_chunk;
859   u32 seed = 0xdeaddabe, j;
860   int i, rv;
861
862   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
863     {
864       if (unformat (input, "fifo-size %d", &fifo_size))
865         ;
866       else if (unformat (input, "total-size %d", &total_size))
867         ;
868       else if (unformat (input, "verbose"))
869         verbose = 1;
870       else if (unformat (input, "overlap"))
871         overlap = 1;
872       else if (unformat (input, "initial-offset %d", &fifo_initial_offset))
873         ;
874       else if (unformat (input, "seed %d", &seed))
875         ;
876       else if (unformat (input, "nsegs %d", &nsegs))
877         ;
878       else if (unformat (input, "no-randomize"))
879         randomize = 0;
880       else if (unformat (input, "in-seq-all"))
881         in_seq_all = 1;
882       else if (unformat (input, "drop"))
883         drop = 1;
884       else
885         {
886           clib_error_t *e = clib_error_return
887             (0, "unknown input `%U'", format_unformat_error, input);
888           clib_error_report (e);
889           return -1;
890         }
891     }
892
893   if (total_size > fifo_size)
894     {
895       clib_warning ("total_size %d greater than fifo size %d", total_size,
896                     fifo_size);
897       return -1;
898     }
899   if (overlap && randomize == 0)
900     {
901       clib_warning ("Can't enqueue in-order with overlap");
902       return -1;
903     }
904
905   /*
906    * Generate data
907    */
908   vec_validate (data_pattern, total_size - 1);
909   for (i = 0; i < vec_len (data_pattern); i++)
910     data_pattern[i] = i & 0xff;
911
912   /*
913    * Generate segments
914    */
915   seg_size = total_size / nsegs;
916   length_so_far = 0;
917   current_offset = randomize;
918   while (length_so_far < total_size)
919     {
920       vec_add2 (generate, tp, 1);
921       len_this_chunk = clib_min (seg_size, total_size - length_so_far);
922       tp->offset = current_offset;
923       tp->len = len_this_chunk;
924
925       if (overlap && (len_this_chunk == seg_size))
926         do
927           {
928             offset_increment = len_this_chunk
929               % (1 + (random_u32 (&seed) % len_this_chunk));
930           }
931         while (offset_increment == 0);
932       else
933         offset_increment = len_this_chunk;
934
935       current_offset += offset_increment;
936       length_so_far = tp->offset + tp->len;
937     }
938
939   /*
940    * Validate segment list. Only valid for non-overlap cases.
941    */
942   if (overlap == 0)
943     fifo_validate_pattern (vm, generate, vec_len (generate));
944
945   if (verbose)
946     {
947       vlib_cli_output (vm, "raw data pattern:");
948       for (i = 0; i < vec_len (generate); i++)
949         {
950           vlib_cli_output (vm, "[%d] offset %u len %u", i,
951                            generate[i].offset, generate[i].len);
952         }
953     }
954
955   /* Randomize data pattern */
956   if (randomize)
957     {
958       for (i = 0; i < vec_len (generate) / 2; i++)
959         {
960           u32 src_index, dst_index;
961           test_pattern_t _tmp, *tmp = &_tmp;
962
963           src_index = random_u32 (&seed) % vec_len (generate);
964           dst_index = random_u32 (&seed) % vec_len (generate);
965
966           tmp[0] = generate[dst_index];
967           generate[dst_index] = generate[src_index];
968           generate[src_index] = tmp[0];
969         }
970       if (verbose)
971         {
972           vlib_cli_output (vm, "randomized data pattern:");
973           for (i = 0; i < vec_len (generate); i++)
974             {
975               vlib_cli_output (vm, "[%d] offset %u len %u", i,
976                                generate[i].offset, generate[i].len);
977             }
978         }
979     }
980
981   /*
982    * Create a fifo and add segments
983    */
984   f = fifo_prepare (fifo_size);
985
986   /* manually set head and tail pointers to validate modular arithmetic */
987   fifo_initial_offset = fifo_initial_offset % fifo_size;
988   f->head = fifo_initial_offset;
989   f->tail = fifo_initial_offset;
990
991   for (i = !randomize; i < vec_len (generate); i++)
992     {
993       tp = generate + i;
994       svm_fifo_enqueue_with_offset (f, fifo_initial_offset + tp->offset,
995                                     tp->len,
996                                     (u8 *) data_pattern + tp->offset);
997     }
998
999   /* Add the first segment in order for non random data */
1000   if (!randomize)
1001     svm_fifo_enqueue_nowait (f, generate[0].len, (u8 *) data_pattern);
1002
1003   /*
1004    * Expected result: one big fat chunk at offset 1 if randomize == 1
1005    */
1006
1007   if (verbose)
1008     vlib_cli_output (vm, "fifo before missing link: %U",
1009                      format_svm_fifo, f, 1 /* verbose */ );
1010
1011   /*
1012    * Add the missing byte if segments were randomized
1013    */
1014   if (randomize)
1015     {
1016       u32 bytes_to_enq = 1;
1017       if (in_seq_all)
1018         bytes_to_enq = total_size;
1019       rv = svm_fifo_enqueue_nowait (f, bytes_to_enq, data_pattern + 0);
1020
1021       if (verbose)
1022         vlib_cli_output (vm, "in-order enqueue returned %d", rv);
1023
1024       TCP_TEST ((rv == total_size), "enqueued %u expected %u", rv,
1025                 total_size);
1026
1027     }
1028
1029   TCP_TEST ((svm_fifo_has_ooo_data (f) == 0), "number of ooo segments %u",
1030             svm_fifo_number_ooo_segments (f));
1031
1032   /*
1033    * Test if peeked data is the same as original data
1034    */
1035   vec_validate (data_buf, vec_len (data_pattern));
1036   svm_fifo_peek (f, 0, vec_len (data_pattern), data_buf);
1037   if (compare_data (data_buf, data_pattern, 0, vec_len (data_pattern), &j))
1038     {
1039       TCP_TEST (0, "[%d] peeked %u expected %u", j, data_buf[j],
1040                 data_pattern[j]);
1041     }
1042   vec_reset_length (data_buf);
1043
1044   /*
1045    * Dequeue or drop all data
1046    */
1047   if (drop)
1048     {
1049       svm_fifo_dequeue_drop (f, vec_len (data_pattern));
1050     }
1051   else
1052     {
1053       svm_fifo_dequeue_nowait (f, vec_len (data_pattern), data_buf);
1054       if (compare_data
1055           (data_buf, data_pattern, 0, vec_len (data_pattern), &j))
1056         {
1057           TCP_TEST (0, "[%d] dequeued %u expected %u", j, data_buf[j],
1058                     data_pattern[j]);
1059         }
1060     }
1061
1062   TCP_TEST ((svm_fifo_max_dequeue (f) == 0), "fifo has %d bytes",
1063             svm_fifo_max_dequeue (f));
1064
1065   svm_fifo_free (f);
1066   vec_free (data_pattern);
1067   vec_free (data_buf);
1068
1069   return 0;
1070 }
1071
1072 static int
1073 tcp_test_fifo4 (vlib_main_t * vm, unformat_input_t * input)
1074 {
1075   svm_fifo_t *f;
1076   u32 fifo_size = 6 << 10;
1077   u32 fifo_initial_offset = 1000000000;
1078   u32 test_n_bytes = 5000, j;
1079   u8 *test_data = 0, *data_buf = 0;
1080   int i, rv, verbose = 0;
1081
1082   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1083     {
1084       if (unformat (input, "verbose"))
1085         verbose = 1;
1086       else
1087         {
1088           clib_error_t *e = clib_error_return
1089             (0, "unknown input `%U'", format_unformat_error, input);
1090           clib_error_report (e);
1091           return -1;
1092         }
1093     }
1094
1095   /*
1096    * Create a fifo and add segments
1097    */
1098   f = fifo_prepare (fifo_size);
1099
1100   /* Set head and tail pointers */
1101   fifo_initial_offset = fifo_initial_offset % fifo_size;
1102   svm_fifo_init_pointers (f, fifo_initial_offset);
1103
1104   vec_validate (test_data, test_n_bytes - 1);
1105   for (i = 0; i < vec_len (test_data); i++)
1106     test_data[i] = i;
1107
1108   for (i = test_n_bytes - 1; i > 0; i--)
1109     {
1110       rv = svm_fifo_enqueue_with_offset (f, fifo_initial_offset + i,
1111                                          sizeof (u8), &test_data[i]);
1112       if (verbose)
1113         vlib_cli_output (vm, "add [%d] [%d, %d]", i, i, i + sizeof (u8));
1114       if (rv)
1115         {
1116           clib_warning ("enqueue returned %d", rv);
1117           svm_fifo_free (f);
1118           vec_free (test_data);
1119           return -1;
1120         }
1121     }
1122
1123   svm_fifo_enqueue_nowait (f, sizeof (u8), &test_data[0]);
1124
1125   vec_validate (data_buf, vec_len (test_data));
1126
1127   svm_fifo_dequeue_nowait (f, vec_len (test_data), data_buf);
1128   rv = compare_data (data_buf, test_data, 0, vec_len (test_data), &j);
1129   if (rv)
1130     vlib_cli_output (vm, "[%d] dequeued %u expected %u", j, data_buf[j],
1131                      test_data[j]);
1132   TCP_TEST ((rv == 0), "dequeued compared to original returned %d", rv);
1133
1134   svm_fifo_free (f);
1135   vec_free (test_data);
1136   return 0;
1137 }
1138
1139 static int
1140 tcp_test_fifo (vlib_main_t * vm, unformat_input_t * input)
1141 {
1142   int res = 0;
1143   char *str;
1144
1145   /* Run all tests */
1146   if (unformat_check_input (input) == UNFORMAT_END_OF_INPUT)
1147     {
1148       res = tcp_test_fifo1 (vm, input);
1149       if (res)
1150         return res;
1151
1152       res = tcp_test_fifo2 (vm);
1153       if (res)
1154         return res;
1155
1156       /*
1157        * Run a number of fifo3 configs
1158        */
1159       str = "nsegs 10 overlap seed 123";
1160       unformat_init_cstring (input, str);
1161       if (tcp_test_fifo3 (vm, input))
1162         return -1;
1163       unformat_free (input);
1164
1165       str = "nsegs 10 overlap seed 123 in-seq-all";
1166       unformat_init_cstring (input, str);
1167       if (tcp_test_fifo3 (vm, input))
1168         return -1;
1169       unformat_free (input);
1170
1171       str = "nsegs 10 overlap seed 123 initial-offset 3917";
1172       unformat_init_cstring (input, str);
1173       if (tcp_test_fifo3 (vm, input))
1174         return -1;
1175       unformat_free (input);
1176
1177       str = "nsegs 10 overlap seed 123 initial-offset 3917 drop";
1178       unformat_init_cstring (input, str);
1179       if (tcp_test_fifo3 (vm, input))
1180         return -1;
1181       unformat_free (input);
1182
1183       str = "nsegs 10 seed 123 initial-offset 3917 drop no-randomize";
1184       unformat_init_cstring (input, str);
1185       if (tcp_test_fifo3 (vm, input))
1186         return -1;
1187       unformat_free (input);
1188     }
1189   else
1190     {
1191       if (unformat (input, "fifo3"))
1192         {
1193           res = tcp_test_fifo3 (vm, input);
1194         }
1195       else if (unformat (input, "fifo2"))
1196         {
1197           res = tcp_test_fifo2 (vm);
1198         }
1199       else if (unformat (input, "fifo1"))
1200         {
1201           res = tcp_test_fifo1 (vm, input);
1202         }
1203       else if (unformat (input, "fifo4"))
1204         {
1205           res = tcp_test_fifo4 (vm, input);
1206         }
1207     }
1208
1209   return res;
1210 }
1211
1212 static int
1213 tcp_test_session (vlib_main_t * vm, unformat_input_t * input)
1214 {
1215   int rv = 0;
1216   tcp_connection_t *tc0;
1217   u8 sst = SESSION_TYPE_IP4_TCP;
1218   ip4_address_t local, remote;
1219   u16 local_port, remote_port;
1220   tcp_main_t *tm = vnet_get_tcp_main ();
1221   int is_add = 1;
1222
1223
1224   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1225     {
1226       if (unformat (input, "del"))
1227         is_add = 0;
1228       else if (unformat (input, "add"))
1229         is_add = 1;
1230       else
1231         break;
1232     }
1233
1234   if (is_add)
1235     {
1236       local.as_u32 = clib_host_to_net_u32 (0x06000101);
1237       remote.as_u32 = clib_host_to_net_u32 (0x06000102);
1238       local_port = clib_host_to_net_u16 (1234);
1239       remote_port = clib_host_to_net_u16 (11234);
1240
1241       pool_get (tm->connections[0], tc0);
1242       memset (tc0, 0, sizeof (*tc0));
1243
1244       tc0->state = TCP_STATE_ESTABLISHED;
1245       tc0->rcv_las = 1;
1246       tc0->c_c_index = tc0 - tm->connections[0];
1247       tc0->c_lcl_port = local_port;
1248       tc0->c_rmt_port = remote_port;
1249       tc0->c_is_ip4 = 1;
1250       tc0->c_thread_index = 0;
1251       tc0->c_lcl_ip4.as_u32 = local.as_u32;
1252       tc0->c_rmt_ip4.as_u32 = remote.as_u32;
1253       tc0->rcv_opts.mss = 1450;
1254       tcp_connection_init_vars (tc0);
1255
1256       TCP_EVT_DBG (TCP_EVT_OPEN, tc0);
1257
1258       if (stream_session_accept (&tc0->connection, 0 /* listener index */ ,
1259                                  sst, 0 /* notify */ ))
1260         clib_warning ("stream_session_accept failed");
1261
1262       stream_session_accept_notify (&tc0->connection);
1263     }
1264   else
1265     {
1266       tc0 = tcp_connection_get (0 /* connection index */ , 0 /* thread */ );
1267       tc0->state = TCP_STATE_CLOSED;
1268       stream_session_disconnect_notify (&tc0->connection);
1269     }
1270
1271   return rv;
1272 }
1273
1274 static clib_error_t *
1275 tcp_test (vlib_main_t * vm,
1276           unformat_input_t * input, vlib_cli_command_t * cmd_arg)
1277 {
1278   int res = 0;
1279
1280   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1281     {
1282       if (unformat (input, "sack"))
1283         {
1284           res = tcp_test_sack (vm, input);
1285         }
1286       else if (unformat (input, "fifo"))
1287         {
1288           res = tcp_test_fifo (vm, input);
1289         }
1290       else if (unformat (input, "session"))
1291         {
1292           res = tcp_test_session (vm, input);
1293         }
1294       else
1295         break;
1296     }
1297
1298   if (res)
1299     {
1300       return clib_error_return (0, "TCP unit test failed");
1301     }
1302   else
1303     {
1304       return 0;
1305     }
1306 }
1307
1308 /* *INDENT-OFF* */
1309 VLIB_CLI_COMMAND (tcp_test_command, static) =
1310 {
1311   .path = "test tcp",
1312   .short_help = "internal tcp unit tests",
1313   .function = tcp_test,
1314 };
1315 /* *INDENT-ON* */
1316
1317
1318 /*
1319  * fd.io coding-style-patch-verification: ON
1320  *
1321  * Local Variables:
1322  * eval: (c-set-style "gnu")
1323  * End:
1324  */