2 * Copyright (c) 2015 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <vnet/ip/ip.h>
18 /* 20 byte TCP + 12 bytes of options (timestamps) = 32 bytes */
24 } __attribute__ ((packed)) rtt_test_header_t;
28 rtt_test_header_t rtt;
30 } __attribute__ ((packed)) rtt_test_packet_t;
33 ip4_address_t src_address, dst_address;
35 f64 n_packets_to_send;
37 f64 send_rate_bits_per_second;
38 f64 send_rate_packets_per_second;
40 f64 packet_accumulator;
44 /* [0] from past, [1] in sequence, [2] from future. */
45 u64 n_packets_received[3];
47 f64 tx_time_stream_created;
48 f64 tx_time_last_sent;
52 u64 rx_expected_sequence_number;
56 /* Including IP & L2 header. */
57 u32 n_bytes_per_packet_on_wire;
59 f64 ave_rtt, rms_rtt, rtt_count;
66 vlib_packet_template_t packet_template;
70 /* Size of encapsulation (e.g. 14 for ethernet). */
77 f64 rms_histogram_units;
79 rtt_test_stream_t stream_history[32];
80 u32 stream_history_index;
82 rtt_test_stream_t * stream_pool;
84 vlib_packet_template_t ack_packet_template;
85 u16 ack_packet_template_ip4_checksum;
88 /* Use 2 IP protocols 253/254 which are assigned for experimental testing. */
90 RTT_TEST_IP_PROTOCOL_DATA = 253,
91 RTT_TEST_IP_PROTOCOL_ACK = 254,
92 } rtt_test_ip_protcol_t;
95 rtt_test_stream_free (vlib_main_t * vm, rtt_test_main_t * tm, rtt_test_stream_t * s)
97 vlib_packet_template_free (vm, &s->packet_template);
98 memset (&s->packet_template, 0, sizeof (s->packet_template));
100 tm->stream_history[tm->stream_history_index++] = s[0];
101 if (tm->stream_history_index >= ARRAY_LEN (tm->stream_history))
102 tm->stream_history_index = 0;
104 s->rtt_histogram = 0;
105 pool_put (tm->stream_pool, s);
108 rtt_test_main_t rtt_test_main;
110 #define foreach_rtt_test_error \
111 _ (packets_received, "packets received") \
112 _ (listener_acks_dropped, "listener acks dropped") \
113 _ (unknown_stream, "unknown stream")
116 #define _(sym,str) RTT_TEST_ERROR_##sym,
117 foreach_rtt_test_error
122 static char * rtt_test_error_strings[] = {
123 #define _(sym,string) string,
124 foreach_rtt_test_error
129 RTT_TEST_RX_NEXT_DROP,
130 RTT_TEST_RX_NEXT_ECHO,
132 } rtt_test_rx_next_t;
135 rtt_test_rx_data (vlib_main_t * vm,
136 vlib_node_runtime_t * node,
137 vlib_frame_t * frame)
139 rtt_test_main_t * tm = &rtt_test_main;
140 uword n_packets = frame->n_vectors;
141 u32 * from, * to_drop, * to_echo;
142 u32 n_left_from, n_left_to_drop, n_left_to_echo;
144 from = vlib_frame_vector_args (frame);
145 n_left_from = n_packets;
147 while (n_left_from > 0)
149 vlib_get_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, to_drop, n_left_to_drop);
150 vlib_get_next_frame (vm, node, RTT_TEST_RX_NEXT_ECHO, to_echo, n_left_to_echo);
152 while (n_left_from > 0 && n_left_to_drop > 0 && n_left_to_echo > 0)
156 rtt_test_header_t * r0;
157 rtt_test_packet_t * ack0;
161 bi0 = to_drop[0] = from[0];
168 p0 = vlib_get_buffer (vm, bi0);
169 ip0 = vlib_buffer_get_current (p0);
171 r0 = ip4_next_header (ip0);
173 p0->error = node->errors[RTT_TEST_ERROR_listener_acks_dropped];
175 ack0 = vlib_packet_template_get_packet (vm, &tm->ack_packet_template, to_echo);
180 sum0 = tm->ack_packet_template_ip4_checksum;
182 ack0->ip4.src_address = ip0->dst_address;
183 sum0 = ip_csum_add_even (sum0, ack0->ip4.src_address.as_u32);
185 ack0->ip4.dst_address = ip0->src_address;
186 sum0 = ip_csum_add_even (sum0, ack0->ip4.dst_address.as_u32);
188 ack0->ip4.checksum = ip_csum_fold (sum0);
190 ASSERT (ack0->ip4.checksum == ip4_header_checksum (&ack0->ip4));
195 vlib_put_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, n_left_to_drop);
196 vlib_put_next_frame (vm, node, RTT_TEST_RX_NEXT_ECHO, n_left_to_echo);
199 return frame->n_vectors;
202 VLIB_REGISTER_NODE (rtt_test_rx_data_node) = {
203 .function = rtt_test_rx_data,
204 .name = "rtt-test-rx-data",
206 .vector_size = sizeof (u32),
208 .n_next_nodes = RTT_TEST_RX_N_NEXT,
210 [RTT_TEST_RX_NEXT_DROP] = "error-drop",
211 [RTT_TEST_RX_NEXT_ECHO] = "ip4-input-no-checksum",
214 .n_errors = RTT_TEST_N_ERROR,
215 .error_strings = rtt_test_error_strings,
219 rtt_test_rx_ack (vlib_main_t * vm,
220 vlib_node_runtime_t * node,
221 vlib_frame_t * frame)
223 rtt_test_main_t * tm = &rtt_test_main;
224 uword n_packets = frame->n_vectors;
225 u32 * from, * to_drop;
226 u32 n_left_from, n_left_to_drop;
227 f64 now = vlib_time_now (vm);
228 vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, rtt_test_rx_data_node.index);
230 from = vlib_frame_vector_args (frame);
231 n_left_from = n_packets;
233 while (n_left_from > 0)
235 vlib_get_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, to_drop, n_left_to_drop);
237 while (n_left_from > 0 && n_left_to_drop > 0)
241 rtt_test_header_t * r0;
242 rtt_test_stream_t * s0;
247 bi0 = to_drop[0] = from[0];
254 p0 = vlib_get_buffer (vm, bi0);
255 ip0 = vlib_buffer_get_current (p0);
257 r0 = ip4_next_header (ip0);
259 p0->error = error_node->errors[RTT_TEST_ERROR_listener_acks_dropped];
261 if (pool_is_free_index (tm->stream_pool, r0->stream_index))
264 s0 = pool_elt_at_index (tm->stream_pool, r0->stream_index);
266 rseq0 = r0->sequence_number;
267 eseq0 = s0->rx_expected_sequence_number;
270 goto out_of_sequence_x1;
272 s0->rx_expected_sequence_number = rseq0 + 1;
273 s0->n_packets_received[1] += 1;
275 vec_add1 (s0->rx_ack_dts, now - r0->time_stamp);
276 _vec_len (s0->rx_ack_dts) -= _vec_len (s0->rx_ack_dts) >= s0->max_n_rx_ack_dts;
279 s0->rx_ack_times[i0] = now;
284 ELOG_TYPE_DECLARE (e) = {
285 .format = "rtt-test: unknown stream %d",
288 struct { u32 stream; } * ed;
289 ed = ELOG_DATA (&vm->elog_main, e);
290 ed->stream = r0->stream_index;
295 i0 = (r0->sequence_number < s0->rx_expected_sequence_number
300 ELOG_TYPE_DECLARE (e) = {
301 .format = "rtt-test: out-of-seq expected %Ld got %Ld",
302 .format_args = "i8i8",
304 struct { u64 expected, got; } * ed;
305 ed = ELOG_DATA (&vm->elog_main, e);
306 ed->expected = s0->rx_expected_sequence_number;
307 ed->got = r0->sequence_number;
310 s0->rx_expected_sequence_number = i0 > 0 ? r0->sequence_number + 1 : s0->rx_expected_sequence_number;
312 s0->n_packets_received[i0] += 1;
314 i0 = r0->sequence_number > 0;
315 s0->rx_ack_times[i0] = now;
318 vlib_put_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, n_left_to_drop);
321 return frame->n_vectors;
324 VLIB_REGISTER_NODE (rtt_test_rx_ack_node) = {
325 .function = rtt_test_rx_ack,
326 .name = "rtt-test-rx-ack",
328 .vector_size = sizeof (u32),
330 .n_next_nodes = RTT_TEST_RX_N_NEXT,
332 [RTT_TEST_RX_NEXT_DROP] = "error-drop",
333 [RTT_TEST_RX_NEXT_ECHO] = "ip4-input-no-checksum",
338 rtt_test_tx_packets (vlib_main_t * vm,
339 vlib_node_runtime_t * node,
340 rtt_test_stream_t * s,
342 uword n_packets_to_send)
344 u32 * to_next, n_this_frame, n_left, n_trace, next, i;
345 rtt_test_packet_t * p;
349 while (n_packets_to_send > 0)
351 vlib_get_next_frame (vm, node, next, to_next, n_left);
353 n_this_frame = clib_min (n_packets_to_send, n_left);
355 for (i = 0; i < n_this_frame; i++)
357 p = vlib_packet_template_get_packet (vm, &s->packet_template, to_next + i);
358 p->rtt.time_stamp = time_now;
359 p->rtt.sequence_number = s->n_packets_sent + i;
362 n_trace = vlib_get_trace_count (vm, node);
365 u32 n = clib_min (n_trace, n_this_frame);
367 vlib_set_trace_count (vm, node, n_trace - n);
368 for (i = 0; i < n_this_frame; i++)
370 b = vlib_get_buffer (vm, to_next[i]);
371 vlib_trace_buffer (vm, node, next, b, /* follow_chain */ 1);
375 s->n_packets_sent += n_this_frame;
376 n_packets_to_send -= n_this_frame;
377 n_left -= n_this_frame;
379 vlib_put_next_frame (vm, node, next, n_left);
384 rtt_test_stream_is_done (rtt_test_stream_t * s, f64 time_now)
386 /* Need to send more packets? */
387 if (s->n_packets_to_send > 0 && s->n_packets_sent < s->n_packets_to_send)
390 /* Received everything we've sent? */
391 if (s->n_packets_received[0] + s->n_packets_received[1] + s->n_packets_received[2] >= s->n_packets_to_send)
394 /* No ACK received after 5 seconds of sending. */
395 if (s->rx_ack_times[0] == 0
396 && s->n_packets_sent > 0
397 && time_now - s->tx_time_stream_created > 5)
400 /* No ACK received after 5 seconds of waiting? */
401 if (time_now - s->rx_ack_times[1] > 5)
407 static_always_inline uword
408 rtt_test_tx_stream (vlib_main_t * vm,
409 vlib_node_runtime_t * node,
410 rtt_test_stream_t * s)
412 rtt_test_main_t * tm = &rtt_test_main;
416 time_now = vlib_time_now (vm);
418 if (rtt_test_stream_is_done (s, time_now))
421 ELOG_TYPE_DECLARE (e) = {
422 .format = "rtt-test: done stream %d",
425 struct { u32 stream_index; } * ed;
426 ed = ELOG_DATA (&vm->elog_main, e);
427 ed->stream_index = s - tm->stream_pool;
430 rtt_test_stream_free (vm, tm, s);
431 if (pool_elts (tm->stream_pool) == 0)
432 vlib_node_set_state (vm, node->node_index, VLIB_NODE_STATE_DISABLED);
436 /* Apply rate limit. */
437 dt = time_now - s->tx_time_last_sent;
438 s->tx_time_last_sent = time_now;
440 n_packets = VLIB_FRAME_SIZE;
441 if (s->send_rate_packets_per_second > 0)
443 s->packet_accumulator += dt * s->send_rate_packets_per_second;
444 n_packets = s->packet_accumulator;
446 /* Never allow accumulator to grow if we get behind. */
447 s->packet_accumulator -= n_packets;
450 /* Apply fixed limit. */
451 if (s->n_packets_to_send > 0
452 && s->n_packets_sent + n_packets > s->n_packets_to_send)
453 n_packets = s->n_packets_to_send - s->n_packets_sent;
455 /* Generate up to one frame's worth of packets. */
456 if (n_packets > VLIB_FRAME_SIZE)
457 n_packets = VLIB_FRAME_SIZE;
460 rtt_test_tx_packets (vm, node, s, time_now, n_packets);
466 rtt_test_tx (vlib_main_t * vm,
467 vlib_node_runtime_t * node,
468 vlib_frame_t * frame)
470 rtt_test_main_t * tm = &rtt_test_main;
471 rtt_test_stream_t * s;
474 pool_foreach (s, tm->stream_pool, ({
475 n_packets += rtt_test_tx_stream (vm, node, s);
481 VLIB_REGISTER_NODE (rtt_test_tx_node) = {
482 .function = rtt_test_tx,
483 .name = "rtt-test-tx",
484 .type = VLIB_NODE_TYPE_INPUT,
485 .state = VLIB_NODE_STATE_DISABLED,
487 .vector_size = sizeof (u32),
491 [0] = "ip4-input-no-checksum",
495 static void rtt_test_stream_compute (rtt_test_main_t * tm, rtt_test_stream_t * s)
499 /* Compute average and standard deviation of RTT time. */
500 if (vec_len (s->rx_ack_dts) == 0)
504 f64 c = vec_len (s->rx_ack_dts);
506 s->ave_rtt = s->rms_rtt = 0;
507 vec_foreach_index (i, s->rx_ack_dts)
509 f64 dt = s->rx_ack_dts[i];
514 s->rms_rtt = sqrt (s->rms_rtt / c - s->ave_rtt*s->ave_rtt);
518 if (! tm->rms_histogram_units)
519 tm->rms_histogram_units = .1;
521 /* Generate historgram. */
522 vec_foreach_index (i, s->rx_ack_dts)
524 i32 bin = flt_round_nearest ((s->rx_ack_dts[i] - s->ave_rtt) / (tm->rms_histogram_units * s->rms_rtt));
525 u32 ib = bin < 0 ? 2*(-bin) + 1 : 2 *bin;
526 vec_validate (s->rtt_histogram, ib);
527 s->rtt_histogram[ib] += 1;
530 if (s->n_packets_sent >= s->n_packets_to_send)
531 vec_free (s->rx_ack_dts);
534 static clib_error_t *
535 do_plot_stream (rtt_test_main_t * tm, rtt_test_stream_t * s, char * file_name, int n)
539 clib_error_t * error = 0;
542 f = (char *) format (0, "%s.%d%c", file_name, n, 0);
543 out = fopen (f, "w");
547 error = clib_error_return_unix (0, "open `%s'", f);
551 rtt_test_stream_compute (tm, s);
552 vec_foreach_index (i, s->rtt_histogram)
554 if (s->rtt_histogram[i] > 0)
556 i32 bi = (i & 1) ? -(i/2) : (i/2);
557 f64 dt = s->ave_rtt + (bi * tm->rms_histogram_units * s->rms_rtt);
558 fformat (out, "%.6e %.6e\n",
559 dt, s->rtt_histogram[i] / s->rtt_count);
562 clib_warning ("wrote `%s'", f);
570 static clib_error_t *
571 do_plot (rtt_test_main_t * tm, char * file_name)
573 rtt_test_stream_t * s;
574 clib_error_t * error = 0;
578 for (i = 0; i < ARRAY_LEN (tm->stream_history); i++)
580 s = tm->stream_history + i;
581 if (s->n_packets_sent > 0)
583 error = do_plot_stream (tm, s, file_name, n++);
589 pool_foreach (s, tm->stream_pool, ({
590 error = do_plot_stream (tm, s, file_name, n++);
598 static clib_error_t *
599 rtt_test_command (vlib_main_t * vm,
600 unformat_input_t * input,
601 vlib_cli_command_t * cmd)
603 rtt_test_main_t * tm = &rtt_test_main;
604 rtt_test_stream_t * s;
609 if (unformat (input, "plot %s", &file_name))
611 clib_error_t * e = do_plot (tm, file_name);
612 vec_free (file_name);
617 pool_get (tm->stream_pool, s);
619 memset (s, 0, sizeof (s[0]));
620 s->n_packets_to_send = 1;
621 s->send_rate_bits_per_second = 1e6;
622 s->n_bytes_payload = 1448;
623 s->max_n_rx_ack_dts = 0;
624 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
626 if (unformat (input, "%U -> %U",
627 unformat_ip4_address, &s->src_address,
628 unformat_ip4_address, &s->dst_address))
630 else if (unformat (input, "count %f", &s->n_packets_to_send))
632 else if (unformat (input, "hist %d", &s->max_n_rx_ack_dts))
634 else if (unformat (input, "rate %f", &s->send_rate_bits_per_second))
636 else if (unformat (input, "size %d", &s->n_bytes_payload))
639 return clib_error_return (0, "parse error: %U", format_unformat_error, input);
642 if (pool_elts (tm->stream_pool) == 1)
643 vlib_node_set_state (vm, rtt_test_tx_node.index, VLIB_NODE_STATE_POLLING);
645 if (! s->max_n_rx_ack_dts)
646 s->max_n_rx_ack_dts = s->n_packets_to_send;
647 vec_validate (s->rx_ack_dts, s->max_n_rx_ack_dts - 1);
648 _vec_len (s->rx_ack_dts) = 0;
650 s->tx_time_stream_created = vlib_time_now (vm);
651 s->tx_time_last_sent = s->tx_time_stream_created;
652 s->n_bytes_per_packet_on_wire
653 = (s->n_bytes_payload
654 + sizeof (rtt_test_header_t)
655 + sizeof (ip4_header_t)
656 + tm->n_encap_bytes);
658 s->send_rate_packets_per_second = s->send_rate_bits_per_second / (s->n_bytes_per_packet_on_wire * BITS (u8));
661 rtt_test_packet_t * t;
664 t = clib_mem_alloc_no_fail (sizeof (t[0]) + s->n_bytes_payload);
665 memset (t, 0, sizeof (t[0]));
667 t->ip4.ip_version_and_header_length = 0x45;
668 t->ip4.length = clib_host_to_net_u16 (sizeof (t[0]) + s->n_bytes_payload);
669 t->ip4.flags_and_fragment_offset = clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
670 t->ip4.protocol = RTT_TEST_IP_PROTOCOL_DATA;
673 t->ip4.src_address = s->src_address;
674 t->ip4.dst_address = s->dst_address;
676 t->ip4.checksum = ip4_header_checksum (&t->ip4);
678 t->rtt.stream_index = s - tm->stream_pool;
680 for (i = 0; i < s->n_bytes_payload; i++)
683 vlib_packet_template_init (vm, &s->packet_template,
684 t, sizeof (t[0]) + s->n_bytes_payload,
685 /* alloc chunk size */ VLIB_FRAME_SIZE,
686 "rtt-test stream %d data", s - tm->stream_pool);
692 ELOG_TYPE_DECLARE (e) = {
693 .format = "rtt-test: start stream %d",
696 struct { u32 stream_index; } * ed;
697 ed = ELOG_DATA (&vm->elog_main, e);
698 ed->stream_index = s - tm->stream_pool;
704 VLIB_CLI_COMMAND (rtt_test_cli_command, static) = {
706 .short_help = "Measure RTT test protocol",
707 .function = rtt_test_command,
710 static u8 * format_rtt_test_stream (u8 * s, va_list * args)
712 rtt_test_stream_t * t = va_arg (*args, rtt_test_stream_t *);
713 uword indent = format_get_indent (s);
715 s = format (s, "%U -> %U",
716 format_ip4_address, &t->src_address,
717 format_ip4_address, &t->dst_address);
719 s = format (s, "\n%U sent %Ld, received: from-past %Ld in-sequence %Ld from-future %Ld",
720 format_white_space, indent,
722 t->n_packets_received[0], t->n_packets_received[1], t->n_packets_received[2]);
724 s = format (s, "\n%U rx-rate %.4e bits/sec",
725 format_white_space, indent,
726 (((f64) (t->n_packets_received[0] + t->n_packets_received[1] + t->n_packets_received[2]) * (f64) t->n_bytes_per_packet_on_wire * BITS (u8))
727 / (t->rx_ack_times[1] - t->rx_ack_times[0])));
729 rtt_test_stream_compute (&rtt_test_main, t);
731 s = format (s, "\n%U rtt %.4e +- %.4e",
732 format_white_space, indent,
733 t->ave_rtt, t->rms_rtt);
738 static clib_error_t *
739 rtt_show_command (vlib_main_t * vm,
740 unformat_input_t * input,
741 vlib_cli_command_t * cmd)
743 rtt_test_main_t * tm = &rtt_test_main;
744 rtt_test_stream_t * s;
747 for (i = 0; i < ARRAY_LEN (tm->stream_history); i++)
749 s = tm->stream_history + i;
750 if (s->n_packets_sent > 0)
751 vlib_cli_output (vm, "%U", format_rtt_test_stream, s);
754 pool_foreach (s, tm->stream_pool, ({
755 vlib_cli_output (vm, "%U", format_rtt_test_stream, s);
761 VLIB_CLI_COMMAND (rtt_show_cli_command, static) = {
763 .short_help = "Show RTT measurements",
764 .function = rtt_show_command,
767 static clib_error_t *
768 rtt_test_init (vlib_main_t * vm)
770 rtt_test_main_t * tm = &rtt_test_main;
772 ip4_register_protocol (RTT_TEST_IP_PROTOCOL_DATA, rtt_test_rx_data_node.index);
773 ip4_register_protocol (RTT_TEST_IP_PROTOCOL_ACK, rtt_test_rx_ack_node.index);
776 rtt_test_packet_t ack;
778 memset (&ack, 0, sizeof (ack));
780 ack.ip4.ip_version_and_header_length = 0x45;
781 ack.ip4.length = clib_host_to_net_u16 (sizeof (ack));
782 ack.ip4.flags_and_fragment_offset = clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
783 ack.ip4.protocol = RTT_TEST_IP_PROTOCOL_ACK;
786 ack.ip4.checksum = ip4_header_checksum (&ack.ip4);
787 tm->ack_packet_template_ip4_checksum = ack.ip4.checksum;
789 vlib_packet_template_init (vm, &tm->ack_packet_template,
792 /* alloc chunk size */ VLIB_FRAME_SIZE,
796 return /* no error */ 0;
799 static VLIB_INIT_FUNCTION (rtt_test_init);
801 static clib_error_t *
802 rtt_test_config (vlib_main_t * vm, unformat_input_t * input)
804 rtt_test_main_t * tm = &rtt_test_main;
805 clib_error_t * error = 0;
807 tm->rms_histogram_units = .1;
809 (14 /* ethernet header */
811 + 12 /* inter packet gap */
814 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
816 if (unformat (input, "rms-histogram-units %f", &tm->rms_histogram_units))
818 else if (unformat (input, "silent"))
821 clib_error ("%U", format_unformat_error, input);
827 VLIB_CONFIG_FUNCTION (rtt_test_config, "rtt-test");