X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Funittest%2Ftcp_test.c;h=25b6744244e6ff42ccdb58278e7edcc6c72ed766;hb=refs%2Fchanges%2F02%2F32202%2F5;hp=e604884d107bb94016772131913ea0f7615d4391;hpb=52814737c351b394d28a8b0ee1544176180f45e0;p=vpp.git diff --git a/src/plugins/unittest/tcp_test.c b/src/plugins/unittest/tcp_test.c index e604884d107..25b6744244e 100644 --- a/src/plugins/unittest/tcp_test.c +++ b/src/plugins/unittest/tcp_test.c @@ -13,6 +13,7 @@ * limitations under the License. */ #include +#include #define TCP_TEST_I(_cond, _comment, _args...) \ ({ \ @@ -85,8 +86,8 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) clib_memset (tc, 0, sizeof (*tc)); + tc->flags |= TCP_CONN_FAST_RECOVERY | TCP_CONN_RECOVERY; tc->snd_una = 0; - tc->snd_una_max = 1000; tc->snd_nxt = 1000; tc->rcv_opts.flags |= TCP_OPTS_FLAG_SACK; tc->snd_mss = 150; @@ -125,18 +126,19 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) TCP_TEST ((hole->start == 900 && hole->end == 1000), "last hole start %u end %u", hole->start, hole->end); TCP_TEST ((sb->sacked_bytes == 400), "sacked bytes %d", sb->sacked_bytes); - TCP_TEST ((sb->snd_una_adv == 0), "snd_una_adv %u", sb->snd_una_adv); + TCP_TEST ((!sb->is_reneging), "is not reneging"); TCP_TEST ((sb->last_sacked_bytes == 400), "last sacked bytes %d", sb->last_sacked_bytes); TCP_TEST ((sb->high_sacked == 900), "high sacked %u", sb->high_sacked); TCP_TEST ((sb->lost_bytes == 300), "lost bytes %u", sb->lost_bytes); /* - * Inject odd blocks + * Inject odd blocks except the last + * */ vec_reset_length (tc->rcv_opts.sacks); - for (i = 0; i < 1000 / 200; i++) + for (i = 0; i < 800 / 200; i++) { vec_add1 (tc->rcv_opts.sacks, sacks[i * 2 + 1]); } @@ -148,46 +150,172 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) sb, tc); hole = scoreboard_first_hole (sb); - TCP_TEST ((pool_elts (sb->holes) == 1), + TCP_TEST ((pool_elts (sb->holes) == 2), "scoreboard has %d holes", pool_elts (sb->holes)); TCP_TEST ((hole->start == 0 && hole->end == 100), "first hole start %u end %u", hole->start, hole->end); - TCP_TEST ((sb->sacked_bytes == 900), "sacked bytes %d", sb->sacked_bytes); - TCP_TEST ((sb->snd_una_adv == 0), "snd_una_adv %u", sb->snd_una_adv); - TCP_TEST ((sb->high_sacked == 1000), "high sacked %u", sb->high_sacked); - TCP_TEST ((sb->last_sacked_bytes == 500), + TCP_TEST ((sb->sacked_bytes == 800), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((!sb->is_reneging), "is not reneging"); + TCP_TEST ((sb->high_sacked == 900), "high sacked %u", sb->high_sacked); + TCP_TEST ((sb->last_sacked_bytes == 400), "last sacked bytes %d", sb->last_sacked_bytes); TCP_TEST ((sb->lost_bytes == 100), "lost bytes %u", sb->lost_bytes); /* - * Ack until byte 100, all bytes are now acked + sacked + * Ack until byte 100 - this is reneging because we should ack until 900 */ tcp_rcv_sacks (tc, 100); if (verbose) vlib_cli_output (vm, "\nack until byte 100:\n%U", format_tcp_scoreboard, sb, tc); - TCP_TEST ((pool_elts (sb->holes) == 0), - "scoreboard has %d elements", pool_elts (sb->holes)); - TCP_TEST ((sb->snd_una_adv == 900), - "snd_una_adv after ack %u", sb->snd_una_adv); + TCP_TEST ((pool_elts (sb->holes) == 1), "scoreboard has %d elements", + pool_elts (sb->holes)); + TCP_TEST ((sb->is_reneging), "is reneging"); + + /* + * Make sure we accept duplicate acks while reneging. + */ + tc->snd_una = 100; + sb->high_rxt = 950; + + block.start = 900; + block.end = 950; + vec_add1 (tc->rcv_opts.sacks, block); + + tcp_rcv_sacks (tc, 100); + TCP_TEST ((pool_elts (sb->holes) == 1), "scoreboard has %d elements", + pool_elts (sb->holes)); + TCP_TEST ((sb->is_reneging), "is reneging"); + TCP_TEST ((sb->last_sacked_bytes == 50), "last sacked bytes %d", + sb->last_sacked_bytes); + TCP_TEST ((sb->rxt_sacked == 50), "last rxt sacked bytes %d", + sb->rxt_sacked); + + /* + * Sack all up to 950 + */ + tcp_rcv_sacks (tc, 950); + TCP_TEST ((sb->high_sacked == 950), "max sacked byte %u", sb->high_sacked); + TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 0), + "last sacked bytes %d", sb->last_sacked_bytes); + TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); + TCP_TEST ((!sb->is_reneging), "is not reneging"); + + /* + * Sack [960 970] [980 990] + */ + sb->high_rxt = 985; + + tc->snd_una = 950; + vec_reset_length (tc->rcv_opts.sacks); + block.start = 960; + block.end = 970; + vec_add1 (tc->rcv_opts.sacks, block); + + block.start = 980; + block.end = 990; + vec_add1 (tc->rcv_opts.sacks, block); + + tcp_rcv_sacks (tc, 950); + TCP_TEST ((sb->high_sacked == 990), "max sacked byte %u", sb->high_sacked); + TCP_TEST ((sb->sacked_bytes == 20), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 20), + "last sacked bytes %d", sb->last_sacked_bytes); + TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); + TCP_TEST ((!sb->is_reneging), "is not reneging"); + TCP_TEST ((sb->rxt_sacked == 15), "last rxt sacked bytes %d", + sb->rxt_sacked); + + /* + * Ack up to 960 (reneging) + [961 971] + */ + tc->rcv_opts.sacks[0].start = 961; + tc->rcv_opts.sacks[0].end = 971; + + tcp_rcv_sacks (tc, 960); + + TCP_TEST ((sb->is_reneging), "is reneging"); + TCP_TEST ((sb->sacked_bytes == 21), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 1), + "last sacked bytes %d", sb->last_sacked_bytes); + TCP_TEST ((sb->rxt_sacked == 11), "last rxt sacked bytes %d", + sb->rxt_sacked); + TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d", + sb->last_bytes_delivered); + + /* + * Ack up to 960 (reneging) + [961 990] + */ + tc->snd_una = 960; + tc->rcv_opts.sacks[0].start = 961; + tc->rcv_opts.sacks[0].end = 990; + + tcp_rcv_sacks (tc, 960); + + TCP_TEST ((sb->is_reneging), "is reneging"); + TCP_TEST ((sb->sacked_bytes == 30), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 9), + "last sacked bytes %d", sb->last_sacked_bytes); + TCP_TEST ((sb->rxt_sacked == 9), "last rxt sacked bytes %d", + sb->rxt_sacked); + + /* + * Sack remaining bytes [990 1000] + */ + tc->rcv_opts.sacks[0].start = 990; + tc->rcv_opts.sacks[0].end = 1000; + + tcp_rcv_sacks (tc, 960); + + TCP_TEST ((sb->is_reneging), "is reneging"); + TCP_TEST ((sb->sacked_bytes == 40), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 10), + "last sacked bytes %d", sb->last_sacked_bytes); + TCP_TEST ((sb->rxt_sacked == 0), "last rxt sacked bytes %d", + sb->rxt_sacked); + TCP_TEST (pool_elts (sb->holes) == 0, "no holes left"); + + /* + * Ack up to 970 no sack blocks + */ + vec_reset_length (tc->rcv_opts.sacks); + tc->rcv_opts.flags &= ~TCP_OPTS_FLAG_SACK; + tcp_rcv_sacks (tc, 970); + + TCP_TEST ((sb->is_reneging), "is reneging"); + TCP_TEST ((sb->sacked_bytes == 30), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 0), + "last sacked bytes %d", sb->last_sacked_bytes); + TCP_TEST ((sb->rxt_sacked == 0), "last rxt sacked bytes %d", + sb->rxt_sacked); + + /* + * Ack all up to 1000 + */ + tc->snd_una = 970; + tcp_rcv_sacks (tc, 1000); TCP_TEST ((sb->high_sacked == 1000), "max sacked byte %u", sb->high_sacked); TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST (sb->last_bytes_delivered == 30, "last bytes delivered %d", + sb->last_bytes_delivered); TCP_TEST ((sb->last_sacked_bytes == 0), "last sacked bytes %d", sb->last_sacked_bytes); TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); + TCP_TEST ((!sb->is_reneging), "is not reneging"); /* * Add new block */ - + tc->flags = 0; + tc->rcv_opts.flags |= TCP_OPTS_FLAG_SACK; vec_reset_length (tc->rcv_opts.sacks); block.start = 1200; block.end = 1300; vec_add1 (tc->rcv_opts.sacks, block); - tc->snd_una_max = 1500; tc->snd_una = 1000; tc->snd_nxt = 1500; tcp_rcv_sacks (tc, 1000); @@ -196,15 +324,12 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) vlib_cli_output (vm, "\nadd [1200, 1300] snd_una_max 1500, snd_una 1000:" " \n%U", format_tcp_scoreboard, sb, tc); - TCP_TEST ((sb->snd_una_adv == 0), - "snd_una_adv after ack %u", sb->snd_una_adv); + TCP_TEST ((!sb->is_reneging), "is not reneging"); TCP_TEST ((pool_elts (sb->holes) == 2), "scoreboard has %d holes", pool_elts (sb->holes)); hole = scoreboard_first_hole (sb); TCP_TEST ((hole->start == 1000 && hole->end == 1200), "first hole start %u end %u", hole->start, hole->end); - TCP_TEST ((sb->snd_una_adv == 0), - "snd_una_adv after ack %u", sb->snd_una_adv); TCP_TEST ((sb->high_sacked == 1300), "max sacked byte %u", sb->high_sacked); hole = scoreboard_last_hole (sb); TCP_TEST ((hole->start == 1300 && hole->end == 1500), @@ -217,29 +342,29 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) */ vec_reset_length (tc->rcv_opts.sacks); - tcp_rcv_sacks (tc, 1200); + /* Ack up to 1300 to avoid reneging */ + tcp_rcv_sacks (tc, 1300); if (verbose) - vlib_cli_output (vm, "\nsb ack up to byte 1200:\n%U", + vlib_cli_output (vm, "\nsb ack up to byte 1300:\n%U", format_tcp_scoreboard, sb, tc); - TCP_TEST ((sb->snd_una_adv == 100), - "snd_una_adv after ack %u", sb->snd_una_adv); TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes); - TCP_TEST ((pool_elts (sb->holes) == 0), + TCP_TEST ((pool_elts (sb->holes) == 1), "scoreboard has %d elements", pool_elts (sb->holes)); TCP_TEST ((sb->last_bytes_delivered == 100), "last bytes delivered %d", sb->last_bytes_delivered); TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); - TCP_TEST ((sb->head == TCP_INVALID_SACK_HOLE_INDEX), "head %u", sb->head); - TCP_TEST ((sb->tail == TCP_INVALID_SACK_HOLE_INDEX), "tail %u", sb->tail); + TCP_TEST ((sb->head != TCP_INVALID_SACK_HOLE_INDEX), "head %u", sb->head); + TCP_TEST ((sb->tail != TCP_INVALID_SACK_HOLE_INDEX), "tail %u", sb->tail); + TCP_TEST ((!sb->is_reneging), "is not reneging"); /* * Add some more blocks and then remove all */ vec_reset_length (tc->rcv_opts.sacks); - tc->snd_una += sb->snd_una_adv; - tc->snd_nxt = tc->snd_una_max = 1900; + tc->snd_una = 1300; + tc->snd_nxt = 1900; for (i = 0; i < 5; i++) { block.start = i * 100 + 1200; @@ -263,8 +388,8 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) */ tc->snd_una = 0; - tc->snd_una_max = 1000; tc->snd_nxt = 1000; + vec_reset_length (tc->rcv_opts.sacks); for (i = 0; i < 5; i++) { vec_add1 (tc->rcv_opts.sacks, sacks[i * 2 + 1]); @@ -272,32 +397,40 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) tc->rcv_opts.n_sack_blocks = vec_len (tc->rcv_opts.sacks); tcp_rcv_sacks (tc, 0); if (verbose) - vlib_cli_output (vm, "\nsb added odd blocks snd_una 0 snd_una_max 1500:" + vlib_cli_output (vm, "\nsb added odd blocks snd_una 0 snd_una_max 1000:" "\n%U", format_tcp_scoreboard, sb, tc); TCP_TEST ((pool_elts (sb->holes) == 5), "scoreboard has %d elements", pool_elts (sb->holes)); TCP_TEST ((sb->lost_bytes == 300), "lost bytes %u", sb->lost_bytes); + hole = scoreboard_last_hole (sb); + TCP_TEST ((hole->end == 900), "last hole end %u", hole->end); + TCP_TEST ((sb->high_sacked == 1000), "high sacked %u", sb->high_sacked); + /* + * Renege bytes from 950 to 1000 + */ tcp_rcv_sacks (tc, 950); if (verbose) vlib_cli_output (vm, "\nack [0, 950]:\n%U", format_tcp_scoreboard, sb, tc); - TCP_TEST ((pool_elts (sb->holes) == 0), - "scoreboard has %d elements", pool_elts (sb->holes)); - TCP_TEST ((sb->snd_una_adv == 50), "snd_una_adv %u", sb->snd_una_adv); - TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes); - TCP_TEST ((sb->last_sacked_bytes == 0), - "last sacked bytes %d", sb->last_sacked_bytes); + TCP_TEST ((pool_elts (sb->holes) == 0), "scoreboard has %d elements", + pool_elts (sb->holes)); + TCP_TEST ((sb->is_reneging), "is reneging"); + TCP_TEST ((sb->sacked_bytes == 50), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 0), "last sacked bytes %d", + sb->last_sacked_bytes); TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); + TCP_TEST ((sb->high_sacked == 1000), "high sacked %u", sb->high_sacked); + + scoreboard_clear (sb); /* * Inject one block, ack it and overlap hole */ tc->snd_una = 0; - tc->snd_una_max = 1000; tc->snd_nxt = 1000; block.start = 100; @@ -317,17 +450,17 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) vlib_cli_output (vm, "\nsb ack [0, 800]:\n%U", format_tcp_scoreboard, sb, tc); - TCP_TEST ((pool_elts (sb->holes) == 0), + TCP_TEST ((pool_elts (sb->holes) == 1), "scoreboard has %d elements", pool_elts (sb->holes)); - TCP_TEST ((sb->snd_una_adv == 0), "snd_una_adv %u", sb->snd_una_adv); + TCP_TEST ((!sb->is_reneging), "is not reneging"); TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes); TCP_TEST ((sb->last_sacked_bytes == 0), "last sacked bytes %d", sb->last_sacked_bytes); TCP_TEST ((sb->last_bytes_delivered == 400), "last bytes delivered %d", sb->last_bytes_delivered); TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); - TCP_TEST ((sb->head == TCP_INVALID_SACK_HOLE_INDEX), "head %u", sb->head); - TCP_TEST ((sb->tail == TCP_INVALID_SACK_HOLE_INDEX), "tail %u", sb->tail); + TCP_TEST ((sb->head != TCP_INVALID_SACK_HOLE_INDEX), "head %u", sb->head); + TCP_TEST ((sb->tail != TCP_INVALID_SACK_HOLE_INDEX), "tail %u", sb->tail); /* * One hole close to head, patch head, split in two and start acking @@ -335,7 +468,6 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) */ scoreboard_clear (sb); tc->snd_una = 0; - tc->snd_una_max = 1000; tc->snd_nxt = 1000; block.start = 500; @@ -368,55 +500,77 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) sb->last_sacked_bytes); TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d", sb->last_bytes_delivered); + /* Hole should be split in 2 lost holes that add up to 300 */ TCP_TEST ((sb->lost_bytes == 300), "lost bytes %u", sb->lost_bytes); + TCP_TEST ((sb->reorder == 7), "reorder %u", sb->reorder); + /* + * Ack [100 300] in two steps + * + * Step 1. Ack [100 200] which delivers 100 of the bytes lost + */ tc->snd_una = 100; tcp_rcv_sacks (tc, 200); + TCP_TEST ((sb->sacked_bytes == 600), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d", + sb->last_bytes_delivered); + TCP_TEST ((sb->lost_bytes == 200), "lost bytes %u", sb->lost_bytes); + + /* + * Step 2. Ack up to 300, although 300 400 is sacked, so this is interpreted + * as reneging. + */ tc->snd_una = 200; tcp_rcv_sacks (tc, 300); if (verbose) - vlib_cli_output (vm, "\nacked [0, 300] in two steps:\n%U", + vlib_cli_output (vm, "\nacked [100, 300] in two steps:\n%U", format_tcp_scoreboard, sb, tc); - TCP_TEST ((sb->sacked_bytes == 500), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->sacked_bytes == 600), "sacked bytes %d", sb->sacked_bytes); TCP_TEST ((sb->lost_bytes == 100), "lost bytes %u", sb->lost_bytes); - TCP_TEST ((sb->last_bytes_delivered == 100), "last bytes delivered %d", + TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d", sb->last_bytes_delivered); + TCP_TEST ((sb->is_reneging), "is reneging"); - tc->snd_una = 400; + /* + * Ack [300 500]. Delivers reneged segment [300 400] and reneges bytes + * above 500 + */ + tc->snd_una = 300; tcp_rcv_sacks (tc, 500); if (verbose) vlib_cli_output (vm, "\nacked [400, 500]:\n%U", format_tcp_scoreboard, sb, tc); TCP_TEST ((pool_elts (sb->holes) == 0), "scoreboard has %d elements", pool_elts (sb->holes)); - TCP_TEST ((sb->sacked_bytes == 0), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->sacked_bytes == 500), "sacked bytes %d", sb->sacked_bytes); TCP_TEST ((sb->last_sacked_bytes == 0), "last sacked bytes %d", sb->last_sacked_bytes); - TCP_TEST ((sb->last_bytes_delivered == 500), "last bytes delivered %d", + TCP_TEST ((sb->last_bytes_delivered == 100), "last bytes delivered %d", sb->last_bytes_delivered); - TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); - TCP_TEST ((sb->snd_una_adv == 500), "snd_una_adv %u", sb->snd_una_adv); + TCP_TEST ((sb->is_reneging), "is reneging"); TCP_TEST ((sb->head == TCP_INVALID_SACK_HOLE_INDEX), "head %u", sb->head); TCP_TEST ((sb->tail == TCP_INVALID_SACK_HOLE_INDEX), "tail %u", sb->tail); /* - * Re-ack high sacked, to make sure last_bytes_delivered and - * snd_una_adv are 0-ed + * Ack up to 1000 to deliver all bytes */ + tc->snd_una = 500; tcp_rcv_sacks (tc, 1000); if (verbose) vlib_cli_output (vm, "\nAck high sacked:\n%U", format_tcp_scoreboard, sb, tc); - TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d", + TCP_TEST ((sb->last_sacked_bytes == 0), "last sacked bytes %d", + sb->last_sacked_bytes); + TCP_TEST ((sb->last_bytes_delivered == 500), "last bytes delivered %d", sb->last_bytes_delivered); - TCP_TEST ((sb->snd_una_adv == 0), "snd_una_adv %u", sb->snd_una_adv); + TCP_TEST ((!sb->is_reneging), "is not reneging"); /* * Add [1200, 1500] and test that [1000, 1200] is lost (bytes condition) * snd_una = 1000 and snd_una_max = 1600 */ tc->snd_una = 1000; - tc->snd_nxt = tc->snd_una_max = 1600; + tc->snd_nxt = 1600; vec_reset_length (tc->rcv_opts.sacks); block.start = 1200; block.end = 1500; @@ -433,8 +587,79 @@ tcp_test_sack_rx (vlib_main_t * vm, unformat_input_t * input) sb->last_sacked_bytes); TCP_TEST ((sb->last_bytes_delivered == 0), "last bytes delivered %d", sb->last_bytes_delivered); - TCP_TEST ((sb->lost_bytes == 200), "lost bytes %u", sb->lost_bytes); - TCP_TEST ((sb->snd_una_adv == 0), "snd_una_adv %u", sb->snd_una_adv); + /* No bytes lost because of reorder */ + TCP_TEST ((sb->lost_bytes == 0), "lost bytes %u", sb->lost_bytes); + TCP_TEST ((sb->reorder == 7), "reorder %u", sb->reorder); + TCP_TEST ((!sb->is_reneging), "is not reneging"); + + /* + * Restart + */ + scoreboard_clear (sb); + vec_reset_length (tc->rcv_opts.sacks); + + /* + * Inject [100 500] + */ + + tc->flags |= TCP_CONN_FAST_RECOVERY | TCP_CONN_RECOVERY; + tc->snd_una = 0; + tc->snd_nxt = 1000; + sb->high_rxt = 0; + + block.start = 100; + block.end = 500; + vec_add1 (tc->rcv_opts.sacks, block); + tc->rcv_opts.n_sack_blocks = vec_len (tc->rcv_opts.sacks); + + tcp_rcv_sacks (tc, 0); + + TCP_TEST ((sb->sacked_bytes == 400), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 400), "last sacked bytes %d", + sb->last_sacked_bytes); + TCP_TEST ((!sb->is_reneging), "is not reneging"); + + /* + * Renege, sack all of the remaining bytes and cover some rxt bytes + */ + sb->high_rxt = 700; + tc->rcv_opts.sacks[0].start = 500; + tc->rcv_opts.sacks[0].end = 1000; + + tcp_rcv_sacks (tc, 100); + + TCP_TEST ((sb->sacked_bytes == 900), "sacked bytes %d", sb->sacked_bytes); + TCP_TEST ((sb->last_sacked_bytes == 500), "last sacked bytes %d", + sb->last_sacked_bytes); + TCP_TEST (sb->is_reneging, "is reneging"); + TCP_TEST ((sb->rxt_sacked == 300), "last rxt sacked bytes %d", + sb->rxt_sacked); + + /* + * Restart + */ + scoreboard_clear (sb); + vec_reset_length (tc->rcv_opts.sacks); + + /* + * Broken sacks: + * block.start > snd_nxt + * && block.start < blk.end + * && block.end <= snd_nxt + */ + tc->flags = 0; + block.start = 2147483647; + block.end = 4294967295; + vec_add1 (tc->rcv_opts.sacks, block); + tc->snd_una = tc->snd_nxt = 1969067947; + + tcp_rcv_sacks (tc, tc->snd_una); + + /* + * Clear + */ + scoreboard_clear (sb); + vec_reset_length (tc->rcv_opts.sacks); return 0; } @@ -615,7 +840,6 @@ static int tcp_test_lookup (vlib_main_t * vm, unformat_input_t * input) { session_main_t *smm = &session_main; - tcp_main_t *tm = &tcp_main; transport_connection_t _tc1, *tc1 = &_tc1, _tc2, *tc2 = &_tc2, *tconn; tcp_connection_t *tc; session_t *s, *s1; @@ -629,9 +853,7 @@ tcp_test_lookup (vlib_main_t * vm, unformat_input_t * input) clib_memset (s, 0, sizeof (*s)); s->session_index = sidx = s - smm->wrk[0].sessions; - pool_get (tm->connections[0], tc); - clib_memset (tc, 0, sizeof (*tc)); - tc->connection.c_index = tc - tm->connections[0]; + tc = tcp_connection_alloc (0); tc->connection.s_index = s->session_index; s->connection_index = tc->connection.c_index; @@ -650,9 +872,7 @@ tcp_test_lookup (vlib_main_t * vm, unformat_input_t * input) clib_memset (s, 0, sizeof (*s)); s->session_index = s - smm->wrk[0].sessions; - pool_get (tm->connections[0], tc); - clib_memset (tc, 0, sizeof (*tc)); - tc->connection.c_index = tc - tm->connections[0]; + tc = tcp_connection_alloc (0); tc->connection.s_index = s->session_index; s->connection_index = tc->connection.c_index; @@ -726,7 +946,6 @@ tcp_test_session (vlib_main_t * vm, unformat_input_t * input) tcp_connection_t *tc0; ip4_address_t local, remote; u16 local_port, remote_port; - tcp_main_t *tm = vnet_get_tcp_main (); int is_add = 1; @@ -747,12 +966,10 @@ tcp_test_session (vlib_main_t * vm, unformat_input_t * input) local_port = clib_host_to_net_u16 (1234); remote_port = clib_host_to_net_u16 (11234); - pool_get (tm->connections[0], tc0); - clib_memset (tc0, 0, sizeof (*tc0)); + tc0 = tcp_connection_alloc (0); tc0->state = TCP_STATE_ESTABLISHED; tc0->rcv_las = 1; - tc0->c_c_index = tc0 - tm->connections[0]; tc0->c_lcl_port = local_port; tc0->c_rmt_port = remote_port; tc0->c_is_ip4 = 1; @@ -762,7 +979,7 @@ tcp_test_session (vlib_main_t * vm, unformat_input_t * input) tc0->rcv_opts.mss = 1450; tcp_connection_init_vars (tc0); - TCP_EVT_DBG (TCP_EVT_OPEN, tc0); + TCP_EVT (TCP_EVT_OPEN, tc0); if (session_stream_accept (&tc0->connection, 0 /* listener index */ , 0 /* thread index */ , 0 /* notify */ )) @@ -786,6 +1003,13 @@ tbt_seq_lt (u32 a, u32 b) return seq_lt (a, b); } +static void +tcp_test_set_time (u32 thread_index, u32 val) +{ + session_main.wrk[thread_index].last_vlib_time = val; + tcp_set_time_now (&tcp_main.wrk_ctx[thread_index], val); +} + static int tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) { @@ -794,7 +1018,7 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) tcp_connection_t _tc, *tc = &_tc; sack_scoreboard_t *sb = &tc->sack_sb; int __clib_unused verbose = 0, i; - u64 rate = 100, burst = 100; + u64 rate = 1000, burst = 100; sack_block_t *sacks = 0; tcp_byte_tracker_t *bt; rb_node_t *root, *rbn; @@ -814,8 +1038,8 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) /* Init data structures */ memset (tc, 0, sizeof (*tc)); - session_main.wrk[thread_index].last_vlib_time = 1; - transport_connection_tx_pacer_update (&tc->connection, rate); + tcp_test_set_time (thread_index, 1); + transport_connection_tx_pacer_update (&tc->connection, rate, 1e6); tcp_bt_init (tc); bt = tc->bt; @@ -825,7 +1049,7 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) */ /* 1) track first burst a time 1 */ - tcp_bt_track_tx (tc); + tcp_bt_track_tx (tc, burst); TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); TCP_TEST (pool_elts (bt->samples) == 1, "should have 1 sample"); @@ -839,7 +1063,7 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) TCP_TEST (!(bts->flags & TCP_BTS_IS_APP_LIMITED), "not app limited"); /* 2) check delivery rate at time 2 */ - session_main.wrk[thread_index].last_vlib_time = 2; + tcp_test_set_time (thread_index, 2); tc->snd_una = tc->snd_nxt = burst; tc->bytes_acked = burst; @@ -849,19 +1073,19 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) TCP_TEST (pool_elts (bt->samples) == 0, "sample should've been consumed"); TCP_TEST (tc->delivered_time == 2, "delivered time should be 2"); TCP_TEST (tc->delivered == burst, "delivered should be 100"); - TCP_TEST (rs->ack_time == 1, "ack time should be 1"); + TCP_TEST (rs->interval_time == 1, "ack time should be 1"); TCP_TEST (rs->delivered == burst, "delivered should be 100"); - TCP_TEST (rs->sample_delivered == 0, "sample delivered should be 0"); - TCP_TEST (rs->tx_rate == rate, "delivered should be %u", rate); + TCP_TEST (rs->prior_delivered == 0, "sample delivered should be 0"); TCP_TEST (!(rs->flags & TCP_BTS_IS_RXT), "not retransmitted"); + TCP_TEST (tc->first_tx_time == 1, "first_tx_time %u", tc->first_tx_time); /* 3) track second burst at time 2 */ - tcp_bt_track_tx (tc); + tcp_bt_track_tx (tc, burst); tc->snd_nxt += burst; /* 4) track second burst at time 3 */ - session_main.wrk[thread_index].last_vlib_time = 3; - tcp_bt_track_tx (tc); + tcp_test_set_time (thread_index, 3); + tcp_bt_track_tx (tc, burst); tc->snd_nxt += burst; TCP_TEST (pool_elts (bt->samples) == 2, "should have 2 samples"); @@ -877,7 +1101,7 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) TCP_TEST (bts->prev == bt->head, "prev should be head"); /* 5) check delivery rate at time 4 */ - session_main.wrk[thread_index].last_vlib_time = 4; + tcp_test_set_time (thread_index, 4); tc->snd_una = tc->snd_nxt; tc->bytes_acked = 2 * burst; @@ -888,12 +1112,11 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) TCP_TEST (tc->delivered_time == 4, "delivered time should be 4"); TCP_TEST (tc->delivered == 3 * burst, "delivered should be 300 is %u", tc->delivered); - TCP_TEST (rs->ack_time == 2, "ack time should be 2"); + TCP_TEST (rs->interval_time == 2, "ack time should be 2"); TCP_TEST (rs->delivered == 2 * burst, "delivered should be 200"); - TCP_TEST (rs->sample_delivered == burst, "delivered should be 100"); - TCP_TEST (rs->tx_rate == rate, "delivered should be %u", rate); + TCP_TEST (rs->prior_delivered == burst, "delivered should be 100"); TCP_TEST (!(rs->flags & TCP_BTS_IS_RXT), "not retransmitted"); - TCP_TEST (!(bts->flags & TCP_BTS_IS_APP_LIMITED), "not app limited"); + TCP_TEST (tc->first_tx_time == 2, "first_tx_time %u", tc->first_tx_time); /* * Track retransmissions @@ -903,25 +1126,25 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) snd_una = tc->snd_una; - /* 1) track first burst a time 4 */ - tcp_bt_track_tx (tc); + /* 1) track first burst at time 4 */ + tcp_bt_track_tx (tc, burst); tc->snd_nxt += burst; /* 2) track second burst at time 5 */ - session_main.wrk[thread_index].last_vlib_time = 5; - tcp_bt_track_tx (tc); + tcp_test_set_time (thread_index, 5); + tcp_bt_track_tx (tc, burst); tc->snd_nxt += burst; /* 3) track third burst at time 6 */ - session_main.wrk[thread_index].last_vlib_time = 6; - tcp_bt_track_tx (tc); + tcp_test_set_time (thread_index, 6); + tcp_bt_track_tx (tc, burst); tc->snd_nxt += burst; /* 4) track fourth burst at time 7 */ - session_main.wrk[thread_index].last_vlib_time = 7; + tcp_test_set_time (thread_index, 7); /* Limited until last burst is acked */ tc->app_limited = snd_una + 4 * burst - 1; - tcp_bt_track_tx (tc); + tcp_bt_track_tx (tc, burst); tc->snd_nxt += burst; /* 5) check delivery rate at time 8 @@ -931,7 +1154,7 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) * [snd_una + burst, snd_una + burst + 10] * [snd_una + 2 * burst + 10, snd_una + 2 * burst + 20] */ - session_main.wrk[thread_index].last_vlib_time = 8; + tcp_test_set_time (thread_index, 8); tc->snd_una += 10; tc->bytes_acked = 10; sb->last_sacked_bytes = 20; @@ -948,19 +1171,23 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) tcp_bt_sample_delivery_rate (tc, rs); TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); - TCP_TEST (pool_elts (bt->samples) == 4, "there should be 4 samples"); + TCP_TEST (pool_elts (bt->samples) == 7, "there should be 7 samples %u", + pool_elts (bt->samples)); TCP_TEST (tc->delivered_time == 8, "delivered time should be 8"); TCP_TEST (tc->delivered == 3 * burst + 30, "delivered should be %u is %u", 3 * burst + 30, tc->delivered); /* All 3 samples have the same delivered number of bytes. So the first is * the reference for delivery estimate. */ - TCP_TEST (rs->ack_time == 4, "ack time should be 4 is %.2f", rs->ack_time); + TCP_TEST (rs->interval_time == 4, "ack time should be 4 is %.2f", + rs->interval_time); TCP_TEST (rs->delivered == 30, "delivered should be 30"); - TCP_TEST (rs->sample_delivered == 3 * burst, + TCP_TEST (rs->prior_delivered == 3 * burst, "sample delivered should be %u", 3 * burst); - TCP_TEST (rs->tx_rate == rate, "delivered should be %u", rate); TCP_TEST (!(rs->flags & TCP_BTS_IS_RXT), "not retransmitted"); TCP_TEST (!(rs->flags & TCP_BTS_IS_APP_LIMITED), "not app limited"); + /* All 3 samples have the same delivered number of bytes. The first + * sets the first tx time */ + TCP_TEST (tc->first_tx_time == 4, "first_tx_time %u", tc->first_tx_time); /* 6) Retransmit and track at time 9 * @@ -972,21 +1199,24 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) * [snd_una + burst + 10, snd_una + 2 * burst + 10] * [snd_una + 2 * burst + 20, snd_una + 4 * burst] */ - session_main.wrk[thread_index].last_vlib_time = 9; + tcp_test_set_time (thread_index, 9); tcp_bt_track_rxt (tc, snd_una + 10, snd_una + burst); TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); /* The retransmit covers everything left from first burst */ - TCP_TEST (pool_elts (bt->samples) == 4, "there should be 4 samples"); + TCP_TEST (pool_elts (bt->samples) == 7, "there should be 7 samples %u", + pool_elts (bt->samples)); tcp_bt_track_rxt (tc, snd_una + burst + 10, snd_una + 2 * burst + 10); TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); - TCP_TEST (pool_elts (bt->samples) == 5, "there should be 5 samples"); + TCP_TEST (pool_elts (bt->samples) == 6, "there should be 6 samples %u", + pool_elts (bt->samples)); /* Retransmit covers last sample entirely so it should be removed */ tcp_bt_track_rxt (tc, snd_una + 2 * burst + 20, snd_una + 4 * burst); TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); - TCP_TEST (pool_elts (bt->samples) == 5, "there should be 5 samples"); + TCP_TEST (pool_elts (bt->samples) == 5, "there should be 5 samples %u", + pool_elts (bt->samples)); vec_validate (min_seqs, 4); min_seqs[0] = snd_una + 10; @@ -1015,7 +1245,7 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) * [snd_una + 2 * burst + 20, snd_una + 2 * burst + 30] * [snd_una + 2 * burst + 50, snd_una + 2 * burst + 60] */ - session_main.wrk[thread_index].last_vlib_time = 10; + tcp_test_set_time (thread_index, 10); tc->snd_una = snd_una + 2 * burst; tc->bytes_acked = 2 * burst - 10; sb->last_sacked_bytes = 20; @@ -1028,28 +1258,32 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) tcp_bt_sample_delivery_rate (tc, rs); TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); - TCP_TEST (pool_elts (bt->samples) == 3, "num samples should be 3 is %u", + TCP_TEST (pool_elts (bt->samples) == 5, "num samples should be 5 is %u", pool_elts (bt->samples)); TCP_TEST (tc->delivered_time == 10, "delivered time should be 10"); TCP_TEST (tc->delivered == 5 * burst + 40, "delivered should be %u is %u", 5 * burst + 40, tc->delivered); - /* A rxt was acked and delivered time for it is 8 (last ack time) */ - TCP_TEST (rs->ack_time == 2, "ack time should be 2 is %.2f", rs->ack_time); + /* A rxt was acked and delivered time for it is 8 (last ack time) so + * ack_time is 2 (8 - 10). However, first_tx_time for rxt was 4 and rxt + * time 9. Therefore snd_time is 5 (9 - 4)*/ + TCP_TEST (rs->interval_time == 5, "ack time should be 5 is %.2f", + rs->interval_time); /* delivered_now - delivered_rxt ~ 5 * burst + 40 - 3 * burst - 30 */ TCP_TEST (rs->delivered == 2 * burst + 10, "delivered should be 210 is %u", rs->delivered); - TCP_TEST (rs->sample_delivered == 3 * burst + 30, + TCP_TEST (rs->prior_delivered == 3 * burst + 30, "sample delivered should be %u", 3 * burst + 30); - TCP_TEST (rs->tx_rate == rate, "delivered should be %u", rate); TCP_TEST (rs->flags & TCP_BTS_IS_RXT, "is retransmitted"); /* Sample is app limited because of the retransmits */ TCP_TEST (rs->flags & TCP_BTS_IS_APP_LIMITED, "is app limited"); TCP_TEST (tc->app_limited, "app limited should be set"); + TCP_TEST (tc->first_tx_time == 9, "first_tx_time %u", tc->first_tx_time); + /* * 8) check delivery rate at time 11 */ - session_main.wrk[thread_index].last_vlib_time = 11; + tcp_test_set_time (thread_index, 11); tc->snd_una = tc->snd_nxt; tc->bytes_acked = 2 * burst; sb->last_sacked_bytes = 0; @@ -1059,23 +1293,39 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) tcp_bt_sample_delivery_rate (tc, rs); TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); - TCP_TEST (pool_elts (bt->samples) == 0, "num samples should be 3 is %u", + TCP_TEST (pool_elts (bt->samples) == 0, "num samples should be 0 is %u", pool_elts (bt->samples)); - TCP_TEST (tc->delivered_time == 11, "delivered time should be 10"); + TCP_TEST (tc->delivered_time == 11, "delivered time should be 11"); TCP_TEST (tc->delivered == 7 * burst, "delivered should be %u is %u", 7 * burst, tc->delivered); - /* Last rxt was at time 8 */ - TCP_TEST (rs->ack_time == 3, "ack time should be 3 is %.2f", rs->ack_time); + /* Delivered time at retransmit was 8 so ack_time is 11 - 8 = 3. However, + * first_tx_time for rxt was 4 and rxt time was 9. Therefore snd_time + * is 9 - 4 = 5 */ + TCP_TEST (rs->interval_time == 5, "ack time should be 5 is %.2f", + rs->interval_time); /* delivered_now - delivered_rxt ~ 7 * burst - 3 * burst - 30. * That's because we didn't retransmit any new segment. */ TCP_TEST (rs->delivered == 4 * burst - 30, "delivered should be 160 is %u", rs->delivered); - TCP_TEST (rs->sample_delivered == 3 * burst + 30, + TCP_TEST (rs->prior_delivered == 3 * burst + 30, "sample delivered should be %u", 3 * burst + 30); - TCP_TEST (rs->tx_rate == rate, "delivered should be %u", rate); TCP_TEST (rs->flags & TCP_BTS_IS_RXT, "is retransmitted"); TCP_TEST (rs->flags & TCP_BTS_IS_APP_LIMITED, "is app limited"); TCP_TEST (tc->app_limited == 0, "app limited should be cleared"); + TCP_TEST (tc->first_tx_time == 9, "first_tx_time %u", tc->first_tx_time); + + /* + * 9) test flush + */ + + tcp_bt_track_tx (tc, burst); + tc->snd_nxt += burst; + + tcp_test_set_time (thread_index, 12); + tcp_bt_track_tx (tc, burst); + tc->snd_nxt += burst; + + tcp_bt_flush_samples (tc); /* * Cleanup @@ -1086,12 +1336,225 @@ tcp_test_delivery (vlib_main_t * vm, unformat_input_t * input) return 0; } +static int +tcp_test_bt (vlib_main_t * vm, unformat_input_t * input) +{ + u32 thread_index = 0; + tcp_rate_sample_t _rs = { 0 }, *rs = &_rs; + tcp_connection_t _tc, *tc = &_tc; + int __clib_unused verbose = 0, i; + tcp_byte_tracker_t *bt; + tcp_bt_sample_t *bts; + u32 head; + sack_block_t *blk; + + /* Init data structures */ + memset (tc, 0, sizeof (*tc)); + tcp_bt_init (tc); + bt = tc->bt; + + /* 1) track first burst at time 1 */ + /* [] --> [0:100] */ + session_main.wrk[thread_index].last_vlib_time = 1; + tcp_bt_track_tx (tc, 100); + tc->snd_nxt += 100; + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 1, "should have 1 sample"); + bts = pool_elt_at_index (bt->samples, bt->head); + head = bt->head; + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->next == TCP_BTS_INVALID_INDEX, "next should be invalid"); + TCP_TEST (bts->prev == TCP_BTS_INVALID_INDEX, "prev should be invalid"); + TCP_TEST (bts->tx_time == 1, "tx time should be 1"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_RXT), "not retransmitted"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + + /* 2) track second butst at time 2 */ + /* --> [0:100][100:200] */ + session_main.wrk[thread_index].last_vlib_time = 2; + tcp_bt_track_tx (tc, 100); + tc->snd_nxt += 100; + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 2, "should have 2 samples"); + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (head == bt->head, "head is not updated"); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->tx_time == 1, "tx time of head should be 1"); + + /* 3) acked partially at time 3 */ + /* ACK:150 */ + /* --> [150:200] */ + session_main.wrk[thread_index].last_vlib_time = 3; + tc->snd_una = 150; + tc->bytes_acked = 150; + tc->sack_sb.last_sacked_bytes = 0; + tcp_bt_sample_delivery_rate (tc, rs); + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 1, "should have 1 sample"); + TCP_TEST (head != bt->head, "head is updated"); + head = bt->head; + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->tx_time == 2, "tx time should be 2"); + + /* 4) track another burst at time 4 */ + /* --> [150:200][200:300] */ + session_main.wrk[thread_index].last_vlib_time = 4; + tcp_bt_track_tx (tc, 100); + tc->snd_nxt += 100; + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 2, "should have 2 samples"); + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (head == bt->head, "head is not updated"); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->tx_time == 2, "tx time of head should be 2"); + + /* 5) track another burst at time 5 */ + /* --> [150:200][200:300][300:400] */ + session_main.wrk[thread_index].last_vlib_time = 5; + tcp_bt_track_tx (tc, 100); + tc->snd_nxt += 100; + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 3, "should have 3 samples"); + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (head == bt->head, "head is not updated"); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->tx_time == 2, "tx time of head should be 2"); + + /* 6) acked with SACK option at time 6 */ + /* ACK:250 + SACK[350:400] */ + /* --> [250:300][300:350][350:400/sacked] */ + session_main.wrk[thread_index].last_vlib_time = 6; + tc->snd_una = 250; + tc->bytes_acked = 100; + tc->sack_sb.last_sacked_bytes = 50; + vec_add2 (tc->rcv_opts.sacks, blk, 1); + blk->start = 350; + blk->end = 400; + tcp_bt_sample_delivery_rate (tc, rs); + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 3, "should have 3 samples"); + TCP_TEST (head != bt->head, "head is updated"); + head = bt->head; + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->tx_time == 4, "tx time of head should be 4"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + bts = pool_elt_at_index (bt->samples, bts->next); + TCP_TEST (bts->tx_time == 5, "tx time of next should be 5"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + bts = pool_elt_at_index (bt->samples, bt->tail); + TCP_TEST (bts->tx_time == 5, "tx time of tail should be 5"); + TCP_TEST ((bts->flags & TCP_BTS_IS_SACKED), "sacked"); + + /* 7) track another burst at time 7 */ + /* --> [250:300][300:350][350:400/sacked][400-500] */ + session_main.wrk[thread_index].last_vlib_time = 7; + tcp_bt_track_tx (tc, 100); + tc->snd_nxt += 100; + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 4, "should have 4 samples"); + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (head == bt->head, "head is not updated"); + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->tx_time == 4, "tx time of head should be 4"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + bts = pool_elt_at_index (bt->samples, bts->next); + TCP_TEST (bts->tx_time == 5, "tx time of next should be 5"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + bts = pool_elt_at_index (bt->samples, bts->next); + TCP_TEST (bts->tx_time == 5, "tx time of next should be 5"); + TCP_TEST ((bts->flags & TCP_BTS_IS_SACKED), "sacked"); + bts = pool_elt_at_index (bt->samples, bt->tail); + TCP_TEST (bts->tx_time == 7, "tx time of tail should be 7"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + + /* 8) retransmit lost one at time 8 */ + /* retransmit [250:300] */ + /* --> [250:300][300:350][350:400/sacked][400-500] */ + session_main.wrk[thread_index].last_vlib_time = 8; + tcp_bt_track_rxt (tc, 250, 300); + tcp_bt_sample_delivery_rate (tc, rs); + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 4, "should have 4 samples"); + TCP_TEST (head == bt->head, "head is not updated"); + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->tx_time == 8, "tx time of head should be 8"); + bts = pool_elt_at_index (bt->samples, bts->next); + TCP_TEST (bts->tx_time == 5, "tx time of next should be 5"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + bts = pool_elt_at_index (bt->samples, bts->next); + TCP_TEST (bts->tx_time == 5, "tx time of next should be 5"); + TCP_TEST ((bts->flags & TCP_BTS_IS_SACKED), "sacked"); + bts = pool_elt_at_index (bt->samples, bt->tail); + TCP_TEST (bts->tx_time == 7, "tx time of tail should be 7"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + + /* 9) acked with SACK option at time 9 */ + /* ACK:350 + SACK[420:450] */ + /* --> [400:420][420:450/sacked][450:400] */ + session_main.wrk[thread_index].last_vlib_time = 6; + tc->snd_una = 400; + tc->bytes_acked = 150; + tc->sack_sb.last_sacked_bytes = 30; + vec_add2 (tc->rcv_opts.sacks, blk, 1); + blk->start = 420; + blk->end = 450; + tcp_bt_sample_delivery_rate (tc, rs); + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 3, "should have 3 samples"); + TCP_TEST (head != bt->head, "head is updated"); + head = bt->head; + bts = pool_elt_at_index (bt->samples, bt->head); + TCP_TEST (bts->min_seq == tc->snd_una, "min seq should be snd_una"); + TCP_TEST (bts->min_seq == 400 && bts->max_seq == 420, "bts [400:420]"); + TCP_TEST (bts->tx_time == 7, "tx time of head should be 7"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + bts = pool_elt_at_index (bt->samples, bts->next); + TCP_TEST (bts->min_seq == 420 && bts->max_seq == 450, "bts [420:450]"); + TCP_TEST (bts->tx_time == 7, "tx time of head should be 7"); + TCP_TEST ((bts->flags & TCP_BTS_IS_SACKED), "sacked"); + bts = pool_elt_at_index (bt->samples, bts->next); + TCP_TEST (bts->min_seq == 450 && bts->max_seq == 500, "bts [450:500]"); + TCP_TEST (bts->tx_time == 7, "tx time of head should be 7"); + TCP_TEST (!(bts->flags & TCP_BTS_IS_SACKED), "not sacked"); + + /* 10) acked partially at time 10 */ + /* ACK:500 */ + /* --> [] */ + session_main.wrk[thread_index].last_vlib_time = 3; + tc->snd_una = 500; + tc->bytes_acked = 100; + tc->sack_sb.last_sacked_bytes = 0; + tcp_bt_sample_delivery_rate (tc, rs); + + TCP_TEST (tcp_bt_is_sane (bt), "tracker should be sane"); + TCP_TEST (pool_elts (bt->samples) == 0, "should have 0 samples"); + TCP_TEST (bt->head == TCP_BTS_INVALID_INDEX, "bt->head is invalidated"); + TCP_TEST (tc->snd_una == tc->snd_nxt, "snd_una == snd_nxt"); + + return 0; +} + static clib_error_t * tcp_test (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd_arg) { int res = 0; + vnet_session_enable_disable (vm, 1); + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "sack")) @@ -1110,6 +1573,10 @@ tcp_test (vlib_main_t * vm, { res = tcp_test_delivery (vm, input); } + else if (unformat (input, "bt")) + { + res = tcp_test_bt (vm, input); + } else if (unformat (input, "all")) { if ((res = tcp_test_sack (vm, input)))