* limitations under the License.
*/
#include <vnet/tcp/tcp.h>
+#include <vnet/tcp/tcp_inlines.h>
#define TCP_TEST_I(_cond, _comment, _args...) \
({ \
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;
sb->rxt_sacked);
/*
- * Sack all up to 1000
+ * 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);
* 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);
*/
vec_reset_length (tc->rcv_opts.sacks);
tc->snd_una = 1300;
- tc->snd_nxt = tc->snd_una_max = 1900;
+ tc->snd_nxt = 1900;
for (i = 0; i < 5; i++)
{
block.start = i * 100 + 1200;
*/
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++)
*/
tc->snd_una = 0;
- tc->snd_una_max = 1000;
tc->snd_nxt = 1000;
block.start = 100;
*/
scoreboard_clear (sb);
tc->snd_una = 0;
- tc->snd_una_max = 1000;
tc->snd_nxt = 1000;
block.start = 500;
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
* 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;
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);
+ /* 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");
/*
tc->flags |= TCP_CONN_FAST_RECOVERY | TCP_CONN_RECOVERY;
tc->snd_una = 0;
- tc->snd_una_max = 1000;
tc->snd_nxt = 1000;
sb->high_rxt = 0;
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;
}
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;
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;
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;
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;
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;
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"))
{
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)))