aboutsummaryrefslogtreecommitdiffstats
path: root/vnet/example
diff options
context:
space:
mode:
Diffstat (limited to 'vnet/example')
-rw-r--r--vnet/example/main_stub.c203
-rw-r--r--vnet/example/rtt_test.c827
2 files changed, 1030 insertions, 0 deletions
diff --git a/vnet/example/main_stub.c b/vnet/example/main_stub.c
new file mode 100644
index 00000000000..c45932baa39
--- /dev/null
+++ b/vnet/example/main_stub.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/pg/pg.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/tcp.h>
+
+#include <vlib/unix/cj.h>
+
+DECLARE_CJ_GLOBAL_LOG
+
+static clib_error_t *
+vnet_example_init (vlib_main_t * vm)
+{
+ clib_error_t * error = 0;
+
+ /* Due to crude comment-out of eliot's smp stuff */
+ vm->heap_size = 256<<20;
+
+ if ((error = vlib_call_init_function (vm, pg_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, ip_main_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, ethernet_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, ethernet_arp_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, osi_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, srp_init)))
+ return error;
+#if DPDK == 0
+ if ((error = vlib_call_init_function (vm, ixge_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, ixgev_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, ige_init)))
+ return error;
+#else
+ if ((error = vlib_call_init_function (vm, dpdk_init)))
+ return error;
+#endif
+
+ if ((error = vlib_call_init_function (vm, dhcp_proxy_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, mpls_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, mpls_interface_init)))
+ return error;
+
+ if ((error = vlib_call_init_function (vm, l2_init)))
+ return error;
+ if ((error = vlib_call_init_function (vm, l2tp_init)))
+ return error;
+
+ if ((error = unix_physmem_init (vm, /* physical_memory_required */ 0)))
+ return error;
+
+ if ((error = unix_physmem_init (vm, /* physical_memory_required */ 0)))
+ return error;
+
+ if ((error = vlib_call_init_function (vm, tuntap_init)))
+ return error;
+
+ vlib_unix_cli_set_prompt ("VNET: ");
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (vnet_example_init);
+
+int main (int argc, char * argv[])
+{
+ clib_mem_init (0, (2ULL << 30));
+ return vlib_unix_main (argc, argv);
+}
+
+#if 0
+#define foreach_tcp_test_error \
+ _ (SEGMENTS_RECEIVED, "segments received")
+
+typedef enum {
+#define _(sym,str) TCP_TEST_ERROR_##sym,
+ foreach_tcp_test_error
+#undef _
+ TCP_TEST_N_ERROR,
+} tcp_test_error_t;
+
+static char * tcp_test_error_strings[] = {
+#define _(sym,string) string,
+ foreach_tcp_test_error
+#undef _
+};
+
+static uword
+tcp_test (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ uword n_packets = frame->n_vectors;
+ u32 * from, * to_next;
+ u32 n_left_from, n_left_to_next, next;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = n_packets;
+ next = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vlib_buffer_t * p0;
+ u32 bi0;
+ u8 error0, next0;
+
+ bi0 = to_next[0] = from[0];
+
+ from += 1;
+ n_left_from -= 1;
+ to_next += 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, bi0);
+
+ clib_warning ("got '%U'", format_vlib_buffer_contents, vm, p0);
+
+ error0 = next0 = 0;
+ p0->error = node->errors[error0];
+
+ if (PREDICT_FALSE (next0 != next))
+ {
+ to_next -= 1;
+ n_left_to_next += 1;
+
+ vlib_put_next_frame (vm, node, next, n_left_to_next);
+
+ next = next0;
+ vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
+ to_next[0] = bi0;
+ to_next += 1;
+ n_left_to_next -= 1;
+ }
+ }
+
+ vlib_put_next_frame (vm, node, next, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (tcp_test_node) = {
+ .function = tcp_test,
+ .name = "tcp-test",
+
+ .vector_size = sizeof (u32),
+
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [0] = "error-drop",
+ },
+
+ .n_errors = TCP_TEST_N_ERROR,
+ .error_strings = tcp_test_error_strings,
+};
+
+static clib_error_t *
+tcp_test_init (vlib_main_t * vm)
+{
+ clib_error_t * error = 0;
+
+ {
+ tcp_listener_registration_t r = {
+ .port = 1234,
+ .flags = TCP_LISTENER_IP4,
+ .data_node_index = tcp_test_node.index,
+ .event_function = 0,
+ };
+
+ tcp_register_listener (vm, &r);
+ }
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (tcp_test_init);
+
+#endif
diff --git a/vnet/example/rtt_test.c b/vnet/example/rtt_test.c
new file mode 100644
index 00000000000..98f0cdf2f6b
--- /dev/null
+++ b/vnet/example/rtt_test.c
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vnet/ip/ip.h>
+#include <math.h>
+
+/* 20 byte TCP + 12 bytes of options (timestamps) = 32 bytes */
+typedef struct {
+ u64 sequence_number;
+ f64 time_stamp;
+ u32 stream_index;
+ u32 unused[3];
+} __attribute__ ((packed)) rtt_test_header_t;
+
+typedef struct {
+ ip4_header_t ip4;
+ rtt_test_header_t rtt;
+ u8 payload[0];
+} __attribute__ ((packed)) rtt_test_packet_t;
+
+typedef struct {
+ ip4_address_t src_address, dst_address;
+
+ f64 n_packets_to_send;
+
+ f64 send_rate_bits_per_second;
+ f64 send_rate_packets_per_second;
+
+ f64 packet_accumulator;
+
+ u64 n_packets_sent;
+
+ /* [0] from past, [1] in sequence, [2] from future. */
+ u64 n_packets_received[3];
+
+ f64 tx_time_stream_created;
+ f64 tx_time_last_sent;
+
+ f64 rx_ack_times[2];
+
+ u64 rx_expected_sequence_number;
+
+ u32 n_bytes_payload;
+
+ /* Including IP & L2 header. */
+ u32 n_bytes_per_packet_on_wire;
+
+ f64 ave_rtt, rms_rtt, rtt_count;
+
+ u32 max_n_rx_ack_dts;
+ f64 * rx_ack_dts;
+
+ u32 * rtt_histogram;
+
+ vlib_packet_template_t packet_template;
+} rtt_test_stream_t;
+
+typedef struct {
+ /* Size of encapsulation (e.g. 14 for ethernet). */
+ u32 n_encap_bytes;
+
+ u32 is_sender;
+
+ u32 verbose;
+
+ f64 rms_histogram_units;
+
+ rtt_test_stream_t stream_history[32];
+ u32 stream_history_index;
+
+ rtt_test_stream_t * stream_pool;
+
+ vlib_packet_template_t ack_packet_template;
+ u16 ack_packet_template_ip4_checksum;
+} rtt_test_main_t;
+
+/* Use 2 IP protocols 253/254 which are assigned for experimental testing. */
+typedef enum {
+ RTT_TEST_IP_PROTOCOL_DATA = 253,
+ RTT_TEST_IP_PROTOCOL_ACK = 254,
+} rtt_test_ip_protcol_t;
+
+always_inline void
+rtt_test_stream_free (vlib_main_t * vm, rtt_test_main_t * tm, rtt_test_stream_t * s)
+{
+ vlib_packet_template_free (vm, &s->packet_template);
+ memset (&s->packet_template, 0, sizeof (s->packet_template));
+
+ tm->stream_history[tm->stream_history_index++] = s[0];
+ if (tm->stream_history_index >= ARRAY_LEN (tm->stream_history))
+ tm->stream_history_index = 0;
+
+ s->rtt_histogram = 0;
+ pool_put (tm->stream_pool, s);
+}
+
+rtt_test_main_t rtt_test_main;
+
+#define foreach_rtt_test_error \
+ _ (packets_received, "packets received") \
+ _ (listener_acks_dropped, "listener acks dropped") \
+ _ (unknown_stream, "unknown stream")
+
+typedef enum {
+#define _(sym,str) RTT_TEST_ERROR_##sym,
+ foreach_rtt_test_error
+#undef _
+ RTT_TEST_N_ERROR,
+} rtt_test_error_t;
+
+static char * rtt_test_error_strings[] = {
+#define _(sym,string) string,
+ foreach_rtt_test_error
+#undef _
+};
+
+typedef enum {
+ RTT_TEST_RX_NEXT_DROP,
+ RTT_TEST_RX_NEXT_ECHO,
+ RTT_TEST_RX_N_NEXT,
+} rtt_test_rx_next_t;
+
+static uword
+rtt_test_rx_data (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+ uword n_packets = frame->n_vectors;
+ u32 * from, * to_drop, * to_echo;
+ u32 n_left_from, n_left_to_drop, n_left_to_echo;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = n_packets;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, to_drop, n_left_to_drop);
+ vlib_get_next_frame (vm, node, RTT_TEST_RX_NEXT_ECHO, to_echo, n_left_to_echo);
+
+ while (n_left_from > 0 && n_left_to_drop > 0 && n_left_to_echo > 0)
+ {
+ vlib_buffer_t * p0;
+ ip4_header_t * ip0;
+ rtt_test_header_t * r0;
+ rtt_test_packet_t * ack0;
+ ip_csum_t sum0;
+ u32 bi0;
+
+ bi0 = to_drop[0] = from[0];
+
+ from += 1;
+ n_left_from -= 1;
+ to_drop += 1;
+ n_left_to_drop -= 1;
+
+ p0 = vlib_get_buffer (vm, bi0);
+ ip0 = vlib_buffer_get_current (p0);
+
+ r0 = ip4_next_header (ip0);
+
+ p0->error = node->errors[RTT_TEST_ERROR_listener_acks_dropped];
+
+ ack0 = vlib_packet_template_get_packet (vm, &tm->ack_packet_template, to_echo);
+
+ to_echo += 1;
+ n_left_to_echo -= 1;
+
+ sum0 = tm->ack_packet_template_ip4_checksum;
+
+ ack0->ip4.src_address = ip0->dst_address;
+ sum0 = ip_csum_add_even (sum0, ack0->ip4.src_address.as_u32);
+
+ ack0->ip4.dst_address = ip0->src_address;
+ sum0 = ip_csum_add_even (sum0, ack0->ip4.dst_address.as_u32);
+
+ ack0->ip4.checksum = ip_csum_fold (sum0);
+
+ ASSERT (ack0->ip4.checksum == ip4_header_checksum (&ack0->ip4));
+
+ ack0->rtt = r0[0];
+ }
+
+ vlib_put_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, n_left_to_drop);
+ vlib_put_next_frame (vm, node, RTT_TEST_RX_NEXT_ECHO, n_left_to_echo);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (rtt_test_rx_data_node) = {
+ .function = rtt_test_rx_data,
+ .name = "rtt-test-rx-data",
+
+ .vector_size = sizeof (u32),
+
+ .n_next_nodes = RTT_TEST_RX_N_NEXT,
+ .next_nodes = {
+ [RTT_TEST_RX_NEXT_DROP] = "error-drop",
+ [RTT_TEST_RX_NEXT_ECHO] = "ip4-input-no-checksum",
+ },
+
+ .n_errors = RTT_TEST_N_ERROR,
+ .error_strings = rtt_test_error_strings,
+};
+
+static uword
+rtt_test_rx_ack (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+ uword n_packets = frame->n_vectors;
+ u32 * from, * to_drop;
+ u32 n_left_from, n_left_to_drop;
+ f64 now = vlib_time_now (vm);
+ vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, rtt_test_rx_data_node.index);
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = n_packets;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, to_drop, n_left_to_drop);
+
+ while (n_left_from > 0 && n_left_to_drop > 0)
+ {
+ vlib_buffer_t * p0;
+ ip4_header_t * ip0;
+ rtt_test_header_t * r0;
+ rtt_test_stream_t * s0;
+ u32 bi0, i0;
+ u64 rseq0, eseq0;
+
+ i0 = 0;
+ bi0 = to_drop[0] = from[0];
+
+ from += 1;
+ n_left_from -= 1;
+ to_drop += 1;
+ n_left_to_drop -= 1;
+
+ p0 = vlib_get_buffer (vm, bi0);
+ ip0 = vlib_buffer_get_current (p0);
+
+ r0 = ip4_next_header (ip0);
+
+ p0->error = error_node->errors[RTT_TEST_ERROR_listener_acks_dropped];
+
+ if (pool_is_free_index (tm->stream_pool, r0->stream_index))
+ goto bad_stream_x1;
+
+ s0 = pool_elt_at_index (tm->stream_pool, r0->stream_index);
+
+ rseq0 = r0->sequence_number;
+ eseq0 = s0->rx_expected_sequence_number;
+
+ if (rseq0 != eseq0)
+ goto out_of_sequence_x1;
+
+ s0->rx_expected_sequence_number = rseq0 + 1;
+ s0->n_packets_received[1] += 1;
+
+ vec_add1 (s0->rx_ack_dts, now - r0->time_stamp);
+ _vec_len (s0->rx_ack_dts) -= _vec_len (s0->rx_ack_dts) >= s0->max_n_rx_ack_dts;
+
+ i0 = rseq0 != 0;
+ s0->rx_ack_times[i0] = now;
+ continue;
+
+ bad_stream_x1:
+ {
+ ELOG_TYPE_DECLARE (e) = {
+ .format = "rtt-test: unknown stream %d",
+ .format_args = "i4",
+ };
+ struct { u32 stream; } * ed;
+ ed = ELOG_DATA (&vm->elog_main, e);
+ ed->stream = r0->stream_index;
+ }
+ continue;
+
+ out_of_sequence_x1:
+ i0 = (r0->sequence_number < s0->rx_expected_sequence_number
+ ? 0
+ : (i0 ? 1 : 2));
+ if (i0 != 1)
+ {
+ ELOG_TYPE_DECLARE (e) = {
+ .format = "rtt-test: out-of-seq expected %Ld got %Ld",
+ .format_args = "i8i8",
+ };
+ struct { u64 expected, got; } * ed;
+ ed = ELOG_DATA (&vm->elog_main, e);
+ ed->expected = s0->rx_expected_sequence_number;
+ ed->got = r0->sequence_number;
+ }
+
+ s0->rx_expected_sequence_number = i0 > 0 ? r0->sequence_number + 1 : s0->rx_expected_sequence_number;
+
+ s0->n_packets_received[i0] += 1;
+
+ i0 = r0->sequence_number > 0;
+ s0->rx_ack_times[i0] = now;
+ }
+
+ vlib_put_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, n_left_to_drop);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (rtt_test_rx_ack_node) = {
+ .function = rtt_test_rx_ack,
+ .name = "rtt-test-rx-ack",
+
+ .vector_size = sizeof (u32),
+
+ .n_next_nodes = RTT_TEST_RX_N_NEXT,
+ .next_nodes = {
+ [RTT_TEST_RX_NEXT_DROP] = "error-drop",
+ [RTT_TEST_RX_NEXT_ECHO] = "ip4-input-no-checksum",
+ },
+};
+
+always_inline void
+rtt_test_tx_packets (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ rtt_test_stream_t * s,
+ f64 time_now,
+ uword n_packets_to_send)
+{
+ u32 * to_next, n_this_frame, n_left, n_trace, next, i;
+ rtt_test_packet_t * p;
+ vlib_buffer_t * b;
+
+ next = 0;
+ while (n_packets_to_send > 0)
+ {
+ vlib_get_next_frame (vm, node, next, to_next, n_left);
+
+ n_this_frame = clib_min (n_packets_to_send, n_left);
+
+ for (i = 0; i < n_this_frame; i++)
+ {
+ p = vlib_packet_template_get_packet (vm, &s->packet_template, to_next + i);
+ p->rtt.time_stamp = time_now;
+ p->rtt.sequence_number = s->n_packets_sent + i;
+ }
+
+ n_trace = vlib_get_trace_count (vm, node);
+ if (n_trace > 0)
+ {
+ u32 n = clib_min (n_trace, n_this_frame);
+
+ vlib_set_trace_count (vm, node, n_trace - n);
+ for (i = 0; i < n_this_frame; i++)
+ {
+ b = vlib_get_buffer (vm, to_next[i]);
+ vlib_trace_buffer (vm, node, next, b, /* follow_chain */ 1);
+ }
+ }
+
+ s->n_packets_sent += n_this_frame;
+ n_packets_to_send -= n_this_frame;
+ n_left -= n_this_frame;
+
+ vlib_put_next_frame (vm, node, next, n_left);
+ }
+}
+
+always_inline uword
+rtt_test_stream_is_done (rtt_test_stream_t * s, f64 time_now)
+{
+ /* Need to send more packets? */
+ if (s->n_packets_to_send > 0 && s->n_packets_sent < s->n_packets_to_send)
+ return 0;
+
+ /* Received everything we've sent? */
+ if (s->n_packets_received[0] + s->n_packets_received[1] + s->n_packets_received[2] >= s->n_packets_to_send)
+ return 1;
+
+ /* No ACK received after 5 seconds of sending. */
+ if (s->rx_ack_times[0] == 0
+ && s->n_packets_sent > 0
+ && time_now - s->tx_time_stream_created > 5)
+ return 1;
+
+ /* No ACK received after 5 seconds of waiting? */
+ if (time_now - s->rx_ack_times[1] > 5)
+ return 1;
+
+ return 0;
+}
+
+static_always_inline uword
+rtt_test_tx_stream (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ rtt_test_stream_t * s)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+ uword n_packets;
+ f64 time_now, dt;
+
+ time_now = vlib_time_now (vm);
+
+ if (rtt_test_stream_is_done (s, time_now))
+ {
+ {
+ ELOG_TYPE_DECLARE (e) = {
+ .format = "rtt-test: done stream %d",
+ .format_args = "i4",
+ };
+ struct { u32 stream_index; } * ed;
+ ed = ELOG_DATA (&vm->elog_main, e);
+ ed->stream_index = s - tm->stream_pool;
+ }
+
+ rtt_test_stream_free (vm, tm, s);
+ if (pool_elts (tm->stream_pool) == 0)
+ vlib_node_set_state (vm, node->node_index, VLIB_NODE_STATE_DISABLED);
+ return 0;
+ }
+
+ /* Apply rate limit. */
+ dt = time_now - s->tx_time_last_sent;
+ s->tx_time_last_sent = time_now;
+
+ n_packets = VLIB_FRAME_SIZE;
+ if (s->send_rate_packets_per_second > 0)
+ {
+ s->packet_accumulator += dt * s->send_rate_packets_per_second;
+ n_packets = s->packet_accumulator;
+
+ /* Never allow accumulator to grow if we get behind. */
+ s->packet_accumulator -= n_packets;
+ }
+
+ /* Apply fixed limit. */
+ if (s->n_packets_to_send > 0
+ && s->n_packets_sent + n_packets > s->n_packets_to_send)
+ n_packets = s->n_packets_to_send - s->n_packets_sent;
+
+ /* Generate up to one frame's worth of packets. */
+ if (n_packets > VLIB_FRAME_SIZE)
+ n_packets = VLIB_FRAME_SIZE;
+
+ if (n_packets > 0)
+ rtt_test_tx_packets (vm, node, s, time_now, n_packets);
+
+ return n_packets;
+}
+
+static uword
+rtt_test_tx (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+ rtt_test_stream_t * s;
+ uword n_packets = 0;
+
+ pool_foreach (s, tm->stream_pool, ({
+ n_packets += rtt_test_tx_stream (vm, node, s);
+ }));
+
+ return n_packets;
+}
+
+VLIB_REGISTER_NODE (rtt_test_tx_node) = {
+ .function = rtt_test_tx,
+ .name = "rtt-test-tx",
+ .type = VLIB_NODE_TYPE_INPUT,
+ .state = VLIB_NODE_STATE_DISABLED,
+
+ .vector_size = sizeof (u32),
+
+ .n_next_nodes = 1,
+ .next_nodes = {
+ [0] = "ip4-input-no-checksum",
+ },
+};
+
+static void rtt_test_stream_compute (rtt_test_main_t * tm, rtt_test_stream_t * s)
+{
+ int i;
+
+ /* Compute average and standard deviation of RTT time. */
+ if (vec_len (s->rx_ack_dts) == 0)
+ return;
+
+ {
+ f64 c = vec_len (s->rx_ack_dts);
+
+ s->ave_rtt = s->rms_rtt = 0;
+ vec_foreach_index (i, s->rx_ack_dts)
+ {
+ f64 dt = s->rx_ack_dts[i];
+ s->ave_rtt += dt;
+ s->rms_rtt += dt*dt;
+ }
+ s->ave_rtt /= c;
+ s->rms_rtt = sqrt (s->rms_rtt / c - s->ave_rtt*s->ave_rtt);
+ s->rtt_count = c;
+ }
+
+ if (! tm->rms_histogram_units)
+ tm->rms_histogram_units = .1;
+
+ /* Generate historgram. */
+ vec_foreach_index (i, s->rx_ack_dts)
+ {
+ i32 bin = flt_round_nearest ((s->rx_ack_dts[i] - s->ave_rtt) / (tm->rms_histogram_units * s->rms_rtt));
+ u32 ib = bin < 0 ? 2*(-bin) + 1 : 2 *bin;
+ vec_validate (s->rtt_histogram, ib);
+ s->rtt_histogram[ib] += 1;
+ }
+
+ if (s->n_packets_sent >= s->n_packets_to_send)
+ vec_free (s->rx_ack_dts);
+}
+
+static clib_error_t *
+do_plot_stream (rtt_test_main_t * tm, rtt_test_stream_t * s, char * file_name, int n)
+{
+ FILE * out;
+ char * f;
+ clib_error_t * error = 0;
+ u32 i;
+
+ f = (char *) format (0, "%s.%d%c", file_name, n, 0);
+ out = fopen (f, "w");
+
+ if (! out)
+ {
+ error = clib_error_return_unix (0, "open `%s'", f);
+ goto done;
+ }
+
+ rtt_test_stream_compute (tm, s);
+ vec_foreach_index (i, s->rtt_histogram)
+ {
+ if (s->rtt_histogram[i] > 0)
+ {
+ i32 bi = (i & 1) ? -(i/2) : (i/2);
+ f64 dt = s->ave_rtt + (bi * tm->rms_histogram_units * s->rms_rtt);
+ fformat (out, "%.6e %.6e\n",
+ dt, s->rtt_histogram[i] / s->rtt_count);
+ }
+ }
+ clib_warning ("wrote `%s'", f);
+
+ done:
+ vec_free (f);
+ fclose (out);
+ return error;
+}
+
+static clib_error_t *
+do_plot (rtt_test_main_t * tm, char * file_name)
+{
+ rtt_test_stream_t * s;
+ clib_error_t * error = 0;
+ int i, n;
+
+ n = 0;
+ for (i = 0; i < ARRAY_LEN (tm->stream_history); i++)
+ {
+ s = tm->stream_history + i;
+ if (s->n_packets_sent > 0)
+ {
+ error = do_plot_stream (tm, s, file_name, n++);
+ if (error)
+ return error;
+ }
+ }
+
+ pool_foreach (s, tm->stream_pool, ({
+ error = do_plot_stream (tm, s, file_name, n++);
+ if (error)
+ return error;
+ }));
+
+ return error;
+}
+
+static clib_error_t *
+rtt_test_command (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+ rtt_test_stream_t * s;
+
+ {
+ char * file_name;
+
+ if (unformat (input, "plot %s", &file_name))
+ {
+ clib_error_t * e = do_plot (tm, file_name);
+ vec_free (file_name);
+ return e;
+ }
+ }
+
+ pool_get (tm->stream_pool, s);
+
+ memset (s, 0, sizeof (s[0]));
+ s->n_packets_to_send = 1;
+ s->send_rate_bits_per_second = 1e6;
+ s->n_bytes_payload = 1448;
+ s->max_n_rx_ack_dts = 0;
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%U -> %U",
+ unformat_ip4_address, &s->src_address,
+ unformat_ip4_address, &s->dst_address))
+ ;
+ else if (unformat (input, "count %f", &s->n_packets_to_send))
+ ;
+ else if (unformat (input, "hist %d", &s->max_n_rx_ack_dts))
+ ;
+ else if (unformat (input, "rate %f", &s->send_rate_bits_per_second))
+ ;
+ else if (unformat (input, "size %d", &s->n_bytes_payload))
+ ;
+ else
+ return clib_error_return (0, "parse error: %U", format_unformat_error, input);
+ }
+
+ if (pool_elts (tm->stream_pool) == 1)
+ vlib_node_set_state (vm, rtt_test_tx_node.index, VLIB_NODE_STATE_POLLING);
+
+ if (! s->max_n_rx_ack_dts)
+ s->max_n_rx_ack_dts = s->n_packets_to_send;
+ vec_validate (s->rx_ack_dts, s->max_n_rx_ack_dts - 1);
+ _vec_len (s->rx_ack_dts) = 0;
+
+ s->tx_time_stream_created = vlib_time_now (vm);
+ s->tx_time_last_sent = s->tx_time_stream_created;
+ s->n_bytes_per_packet_on_wire
+ = (s->n_bytes_payload
+ + sizeof (rtt_test_header_t)
+ + sizeof (ip4_header_t)
+ + tm->n_encap_bytes);
+
+ s->send_rate_packets_per_second = s->send_rate_bits_per_second / (s->n_bytes_per_packet_on_wire * BITS (u8));
+
+ {
+ rtt_test_packet_t * t;
+ int i;
+
+ t = clib_mem_alloc_no_fail (sizeof (t[0]) + s->n_bytes_payload);
+ memset (t, 0, sizeof (t[0]));
+
+ t->ip4.ip_version_and_header_length = 0x45;
+ t->ip4.length = clib_host_to_net_u16 (sizeof (t[0]) + s->n_bytes_payload);
+ t->ip4.flags_and_fragment_offset = clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
+ t->ip4.protocol = RTT_TEST_IP_PROTOCOL_DATA;
+ t->ip4.ttl = 64;
+
+ t->ip4.src_address = s->src_address;
+ t->ip4.dst_address = s->dst_address;
+
+ t->ip4.checksum = ip4_header_checksum (&t->ip4);
+
+ t->rtt.stream_index = s - tm->stream_pool;
+
+ for (i = 0; i < s->n_bytes_payload; i++)
+ t->payload[i] = i;
+
+ vlib_packet_template_init (vm, &s->packet_template,
+ t, sizeof (t[0]) + s->n_bytes_payload,
+ /* alloc chunk size */ VLIB_FRAME_SIZE,
+ "rtt-test stream %d data", s - tm->stream_pool);
+
+ clib_mem_free (t);
+ }
+
+ {
+ ELOG_TYPE_DECLARE (e) = {
+ .format = "rtt-test: start stream %d",
+ .format_args = "i4",
+ };
+ struct { u32 stream_index; } * ed;
+ ed = ELOG_DATA (&vm->elog_main, e);
+ ed->stream_index = s - tm->stream_pool;
+ }
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (rtt_test_cli_command, static) = {
+ .path = "test rtt",
+ .short_help = "Measure RTT test protocol",
+ .function = rtt_test_command,
+};
+
+static u8 * format_rtt_test_stream (u8 * s, va_list * args)
+{
+ rtt_test_stream_t * t = va_arg (*args, rtt_test_stream_t *);
+ uword indent = format_get_indent (s);
+
+ s = format (s, "%U -> %U",
+ format_ip4_address, &t->src_address,
+ format_ip4_address, &t->dst_address);
+
+ s = format (s, "\n%U sent %Ld, received: from-past %Ld in-sequence %Ld from-future %Ld",
+ format_white_space, indent,
+ t->n_packets_sent,
+ t->n_packets_received[0], t->n_packets_received[1], t->n_packets_received[2]);
+
+ s = format (s, "\n%U rx-rate %.4e bits/sec",
+ format_white_space, indent,
+ (((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))
+ / (t->rx_ack_times[1] - t->rx_ack_times[0])));
+
+ rtt_test_stream_compute (&rtt_test_main, t);
+
+ s = format (s, "\n%U rtt %.4e +- %.4e",
+ format_white_space, indent,
+ t->ave_rtt, t->rms_rtt);
+
+ return s;
+}
+
+static clib_error_t *
+rtt_show_command (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+ rtt_test_stream_t * s;
+ int i;
+
+ for (i = 0; i < ARRAY_LEN (tm->stream_history); i++)
+ {
+ s = tm->stream_history + i;
+ if (s->n_packets_sent > 0)
+ vlib_cli_output (vm, "%U", format_rtt_test_stream, s);
+ }
+
+ pool_foreach (s, tm->stream_pool, ({
+ vlib_cli_output (vm, "%U", format_rtt_test_stream, s);
+ }));
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (rtt_show_cli_command, static) = {
+ .path = "show rtt",
+ .short_help = "Show RTT measurements",
+ .function = rtt_show_command,
+};
+
+static clib_error_t *
+rtt_test_init (vlib_main_t * vm)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+
+ ip4_register_protocol (RTT_TEST_IP_PROTOCOL_DATA, rtt_test_rx_data_node.index);
+ ip4_register_protocol (RTT_TEST_IP_PROTOCOL_ACK, rtt_test_rx_ack_node.index);
+
+ {
+ rtt_test_packet_t ack;
+
+ memset (&ack, 0, sizeof (ack));
+
+ ack.ip4.ip_version_and_header_length = 0x45;
+ ack.ip4.length = clib_host_to_net_u16 (sizeof (ack));
+ ack.ip4.flags_and_fragment_offset = clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
+ ack.ip4.protocol = RTT_TEST_IP_PROTOCOL_ACK;
+ ack.ip4.ttl = 64;
+
+ ack.ip4.checksum = ip4_header_checksum (&ack.ip4);
+ tm->ack_packet_template_ip4_checksum = ack.ip4.checksum;
+
+ vlib_packet_template_init (vm, &tm->ack_packet_template,
+ &ack,
+ sizeof (ack),
+ /* alloc chunk size */ VLIB_FRAME_SIZE,
+ "rtt-test ack");
+ }
+
+ return /* no error */ 0;
+}
+
+static VLIB_INIT_FUNCTION (rtt_test_init);
+
+static clib_error_t *
+rtt_test_config (vlib_main_t * vm, unformat_input_t * input)
+{
+ rtt_test_main_t * tm = &rtt_test_main;
+ clib_error_t * error = 0;
+
+ tm->rms_histogram_units = .1;
+ tm->n_encap_bytes =
+ (14 /* ethernet header */
+ + 8 /* preamble */
+ + 12 /* inter packet gap */
+ + 4 /* crc */);
+ tm->verbose = 1;
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "rms-histogram-units %f", &tm->rms_histogram_units))
+ ;
+ else if (unformat (input, "silent"))
+ tm->verbose = 0;
+ else
+ clib_error ("%U", format_unformat_error, input);
+ }
+
+ return error;
+}
+
+VLIB_CONFIG_FUNCTION (rtt_test_config, "rtt-test");