Initial commit of vpp code.
[vpp.git] / vnet / example / rtt_test.c
1 /*
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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vnet/ip/ip.h>
16 #include <math.h>
17
18 /* 20 byte TCP + 12 bytes of options (timestamps) = 32 bytes */
19 typedef struct {
20   u64 sequence_number;
21   f64 time_stamp;
22   u32 stream_index;
23   u32 unused[3];
24 } __attribute__ ((packed)) rtt_test_header_t;
25
26 typedef struct {
27   ip4_header_t ip4;
28   rtt_test_header_t rtt;
29   u8 payload[0];
30 } __attribute__ ((packed)) rtt_test_packet_t;
31
32 typedef struct {
33   ip4_address_t src_address, dst_address;
34
35   f64 n_packets_to_send;
36
37   f64 send_rate_bits_per_second;
38   f64 send_rate_packets_per_second;
39
40   f64 packet_accumulator;
41
42   u64 n_packets_sent;
43
44   /* [0] from past, [1] in sequence, [2] from future. */
45   u64 n_packets_received[3];
46
47   f64 tx_time_stream_created;
48   f64 tx_time_last_sent;
49
50   f64 rx_ack_times[2];
51
52   u64 rx_expected_sequence_number;
53
54   u32 n_bytes_payload;
55
56   /* Including IP & L2 header. */
57   u32 n_bytes_per_packet_on_wire;
58
59   f64 ave_rtt, rms_rtt, rtt_count;
60
61   u32 max_n_rx_ack_dts;
62   f64 * rx_ack_dts;
63
64   u32 * rtt_histogram;
65
66   vlib_packet_template_t packet_template;
67 } rtt_test_stream_t;
68
69 typedef struct {
70   /* Size of encapsulation (e.g. 14 for ethernet). */
71   u32 n_encap_bytes;
72
73   u32 is_sender;
74
75   u32 verbose;
76
77   f64 rms_histogram_units;
78
79   rtt_test_stream_t stream_history[32];
80   u32 stream_history_index;
81
82   rtt_test_stream_t * stream_pool;
83
84   vlib_packet_template_t ack_packet_template;
85   u16 ack_packet_template_ip4_checksum;
86 } rtt_test_main_t;
87
88 /* Use 2 IP protocols 253/254 which are assigned for experimental testing. */
89 typedef enum {
90   RTT_TEST_IP_PROTOCOL_DATA = 253,
91   RTT_TEST_IP_PROTOCOL_ACK = 254,
92 } rtt_test_ip_protcol_t;
93
94 always_inline void
95 rtt_test_stream_free (vlib_main_t * vm, rtt_test_main_t * tm, rtt_test_stream_t * s)
96 {
97   vlib_packet_template_free (vm, &s->packet_template);
98   memset (&s->packet_template, 0, sizeof (s->packet_template));
99
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;
103
104   s->rtt_histogram = 0;
105   pool_put (tm->stream_pool, s);
106 }
107
108 rtt_test_main_t rtt_test_main;
109
110 #define foreach_rtt_test_error                          \
111   _ (packets_received, "packets received")              \
112   _ (listener_acks_dropped, "listener acks dropped")    \
113   _ (unknown_stream, "unknown stream")
114
115 typedef enum {
116 #define _(sym,str) RTT_TEST_ERROR_##sym,
117   foreach_rtt_test_error
118 #undef _
119   RTT_TEST_N_ERROR,
120 } rtt_test_error_t;
121
122 static char * rtt_test_error_strings[] = {
123 #define _(sym,string) string,
124   foreach_rtt_test_error
125 #undef _
126 };
127
128 typedef enum {
129   RTT_TEST_RX_NEXT_DROP,
130   RTT_TEST_RX_NEXT_ECHO,
131   RTT_TEST_RX_N_NEXT,
132 } rtt_test_rx_next_t;
133
134 static uword
135 rtt_test_rx_data (vlib_main_t * vm,
136                   vlib_node_runtime_t * node,
137                   vlib_frame_t * frame)
138 {
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;
143
144   from = vlib_frame_vector_args (frame);
145   n_left_from = n_packets;
146   
147   while (n_left_from > 0)
148     {
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);
151
152       while (n_left_from > 0 && n_left_to_drop > 0 && n_left_to_echo > 0)
153         {
154           vlib_buffer_t * p0;
155           ip4_header_t * ip0;
156           rtt_test_header_t * r0;
157           rtt_test_packet_t * ack0;
158           ip_csum_t sum0;
159           u32 bi0;
160       
161           bi0 = to_drop[0] = from[0];
162
163           from += 1;
164           n_left_from -= 1;
165           to_drop += 1;
166           n_left_to_drop -= 1;
167       
168           p0 = vlib_get_buffer (vm, bi0);
169           ip0 = vlib_buffer_get_current (p0);
170
171           r0 = ip4_next_header (ip0);
172
173           p0->error = node->errors[RTT_TEST_ERROR_listener_acks_dropped];
174
175           ack0 = vlib_packet_template_get_packet (vm, &tm->ack_packet_template, to_echo);
176
177           to_echo += 1;
178           n_left_to_echo -= 1;
179
180           sum0 = tm->ack_packet_template_ip4_checksum;
181
182           ack0->ip4.src_address = ip0->dst_address;
183           sum0 = ip_csum_add_even (sum0, ack0->ip4.src_address.as_u32);
184
185           ack0->ip4.dst_address = ip0->src_address;
186           sum0 = ip_csum_add_even (sum0, ack0->ip4.dst_address.as_u32);
187
188           ack0->ip4.checksum = ip_csum_fold (sum0);
189
190           ASSERT (ack0->ip4.checksum == ip4_header_checksum (&ack0->ip4));
191
192           ack0->rtt = r0[0];
193         }
194   
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);
197     }
198
199   return frame->n_vectors;
200 }
201
202 VLIB_REGISTER_NODE (rtt_test_rx_data_node) = {
203   .function = rtt_test_rx_data,
204   .name = "rtt-test-rx-data",
205
206   .vector_size = sizeof (u32),
207
208   .n_next_nodes = RTT_TEST_RX_N_NEXT,
209   .next_nodes = {
210     [RTT_TEST_RX_NEXT_DROP] = "error-drop",
211     [RTT_TEST_RX_NEXT_ECHO] = "ip4-input-no-checksum",
212   },
213
214   .n_errors = RTT_TEST_N_ERROR,
215   .error_strings = rtt_test_error_strings,
216 };
217
218 static uword
219 rtt_test_rx_ack (vlib_main_t * vm,
220                  vlib_node_runtime_t * node,
221                  vlib_frame_t * frame)
222 {
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);
229
230   from = vlib_frame_vector_args (frame);
231   n_left_from = n_packets;
232   
233   while (n_left_from > 0)
234     {
235       vlib_get_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, to_drop, n_left_to_drop);
236
237       while (n_left_from > 0 && n_left_to_drop > 0)
238         {
239           vlib_buffer_t * p0;
240           ip4_header_t * ip0;
241           rtt_test_header_t * r0;
242           rtt_test_stream_t * s0;
243           u32 bi0, i0;
244           u64 rseq0, eseq0;
245       
246           i0 = 0;
247           bi0 = to_drop[0] = from[0];
248
249           from += 1;
250           n_left_from -= 1;
251           to_drop += 1;
252           n_left_to_drop -= 1;
253       
254           p0 = vlib_get_buffer (vm, bi0);
255           ip0 = vlib_buffer_get_current (p0);
256
257           r0 = ip4_next_header (ip0);
258
259           p0->error = error_node->errors[RTT_TEST_ERROR_listener_acks_dropped];
260
261           if (pool_is_free_index (tm->stream_pool, r0->stream_index))
262             goto bad_stream_x1;
263
264           s0 = pool_elt_at_index (tm->stream_pool, r0->stream_index);
265
266           rseq0 = r0->sequence_number;
267           eseq0 = s0->rx_expected_sequence_number;
268
269           if (rseq0 != eseq0)
270             goto out_of_sequence_x1;
271
272           s0->rx_expected_sequence_number = rseq0 + 1;
273           s0->n_packets_received[1] += 1;
274           
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;
277
278           i0 = rseq0 != 0;
279           s0->rx_ack_times[i0] = now;
280           continue;
281
282         bad_stream_x1:
283           {
284             ELOG_TYPE_DECLARE (e) = {
285               .format = "rtt-test: unknown stream %d",
286               .format_args = "i4",
287             };
288             struct { u32 stream; } * ed;
289             ed = ELOG_DATA (&vm->elog_main, e);
290             ed->stream = r0->stream_index;
291           }
292           continue;
293
294         out_of_sequence_x1:
295           i0 = (r0->sequence_number < s0->rx_expected_sequence_number
296                 ? 0
297                 : (i0 ? 1 : 2));
298           if (i0 != 1)
299             {
300               ELOG_TYPE_DECLARE (e) = {
301                 .format = "rtt-test: out-of-seq expected %Ld got %Ld",
302                 .format_args = "i8i8",
303               };
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;
308             }
309
310           s0->rx_expected_sequence_number = i0 > 0 ? r0->sequence_number + 1 : s0->rx_expected_sequence_number;
311
312           s0->n_packets_received[i0] += 1;
313
314           i0 = r0->sequence_number > 0;
315           s0->rx_ack_times[i0] = now;
316         }
317   
318       vlib_put_next_frame (vm, node, RTT_TEST_RX_NEXT_DROP, n_left_to_drop);
319     }
320
321   return frame->n_vectors;
322 }
323
324 VLIB_REGISTER_NODE (rtt_test_rx_ack_node) = {
325   .function = rtt_test_rx_ack,
326   .name = "rtt-test-rx-ack",
327
328   .vector_size = sizeof (u32),
329
330   .n_next_nodes = RTT_TEST_RX_N_NEXT,
331   .next_nodes = {
332     [RTT_TEST_RX_NEXT_DROP] = "error-drop",
333     [RTT_TEST_RX_NEXT_ECHO] = "ip4-input-no-checksum",
334   },
335 };
336
337 always_inline void
338 rtt_test_tx_packets (vlib_main_t * vm,
339                      vlib_node_runtime_t * node,
340                      rtt_test_stream_t * s,
341                      f64 time_now,
342                      uword n_packets_to_send)
343 {
344   u32 * to_next, n_this_frame, n_left, n_trace, next, i;
345   rtt_test_packet_t * p;
346   vlib_buffer_t * b;
347
348   next = 0;
349   while (n_packets_to_send > 0)
350     {
351       vlib_get_next_frame (vm, node, next, to_next, n_left);
352
353       n_this_frame = clib_min (n_packets_to_send, n_left);
354
355       for (i = 0; i < n_this_frame; i++)
356         {
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;
360         }
361
362       n_trace = vlib_get_trace_count (vm, node);
363       if (n_trace > 0)
364         {
365           u32 n = clib_min (n_trace, n_this_frame);
366
367           vlib_set_trace_count (vm, node, n_trace - n);
368           for (i = 0; i < n_this_frame; i++)
369             {
370               b = vlib_get_buffer (vm, to_next[i]);
371               vlib_trace_buffer (vm, node, next, b, /* follow_chain */ 1);
372             }
373         }
374
375       s->n_packets_sent += n_this_frame;
376       n_packets_to_send -= n_this_frame;
377       n_left -= n_this_frame;
378
379       vlib_put_next_frame (vm, node, next, n_left);
380     }
381 }
382
383 always_inline uword
384 rtt_test_stream_is_done (rtt_test_stream_t * s, f64 time_now)
385 {
386   /* Need to send more packets? */
387   if (s->n_packets_to_send > 0 && s->n_packets_sent < s->n_packets_to_send)
388     return 0;
389
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)
392     return 1;
393
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)
398     return 1;
399
400   /* No ACK received after 5 seconds of waiting? */
401   if (time_now - s->rx_ack_times[1] > 5)
402     return 1;
403
404   return 0;
405 }
406
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)
411 {
412   rtt_test_main_t * tm = &rtt_test_main;
413   uword n_packets;
414   f64 time_now, dt;
415
416   time_now = vlib_time_now (vm);
417
418   if (rtt_test_stream_is_done (s, time_now))
419     {
420       {
421         ELOG_TYPE_DECLARE (e) = {
422           .format = "rtt-test: done stream %d",
423           .format_args = "i4",
424         };
425         struct { u32 stream_index; } * ed;
426         ed = ELOG_DATA (&vm->elog_main, e);
427         ed->stream_index = s - tm->stream_pool;
428       }
429
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);
433       return 0;
434     }
435
436   /* Apply rate limit. */
437   dt = time_now - s->tx_time_last_sent;
438   s->tx_time_last_sent = time_now;
439
440   n_packets = VLIB_FRAME_SIZE;
441   if (s->send_rate_packets_per_second > 0)
442     {
443       s->packet_accumulator += dt * s->send_rate_packets_per_second;
444       n_packets = s->packet_accumulator;
445
446       /* Never allow accumulator to grow if we get behind. */
447       s->packet_accumulator -= n_packets;
448     }
449
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;
454
455   /* Generate up to one frame's worth of packets. */
456   if (n_packets > VLIB_FRAME_SIZE)
457     n_packets = VLIB_FRAME_SIZE;
458
459   if (n_packets > 0)
460     rtt_test_tx_packets (vm, node, s, time_now, n_packets);
461
462   return n_packets;
463 }
464
465 static uword
466 rtt_test_tx (vlib_main_t * vm,
467              vlib_node_runtime_t * node,
468              vlib_frame_t * frame)
469 {
470   rtt_test_main_t * tm = &rtt_test_main;
471   rtt_test_stream_t * s;
472   uword n_packets = 0;
473
474   pool_foreach (s, tm->stream_pool, ({
475     n_packets += rtt_test_tx_stream (vm, node, s);
476   }));
477
478   return n_packets;
479 }
480
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,
486
487   .vector_size = sizeof (u32),
488
489   .n_next_nodes = 1,
490   .next_nodes = {
491     [0] = "ip4-input-no-checksum",
492   },
493 };
494
495 static void rtt_test_stream_compute (rtt_test_main_t * tm, rtt_test_stream_t * s)
496 {
497   int i;
498
499   /* Compute average and standard deviation of RTT time. */
500   if (vec_len (s->rx_ack_dts) == 0)
501     return;
502
503   {
504     f64 c = vec_len (s->rx_ack_dts);
505
506     s->ave_rtt = s->rms_rtt = 0;
507     vec_foreach_index (i, s->rx_ack_dts)
508       {
509         f64 dt = s->rx_ack_dts[i];
510         s->ave_rtt += dt;
511         s->rms_rtt += dt*dt;
512       }
513     s->ave_rtt /= c;
514     s->rms_rtt = sqrt (s->rms_rtt / c - s->ave_rtt*s->ave_rtt);
515     s->rtt_count = c;
516   }
517
518   if (! tm->rms_histogram_units)
519     tm->rms_histogram_units = .1;
520
521   /* Generate historgram. */
522   vec_foreach_index (i, s->rx_ack_dts)
523     {
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;
528     }  
529
530   if (s->n_packets_sent >= s->n_packets_to_send)
531     vec_free (s->rx_ack_dts);
532 }
533
534 static clib_error_t *
535 do_plot_stream (rtt_test_main_t * tm, rtt_test_stream_t * s, char * file_name, int n)
536 {
537   FILE * out;
538   char * f;
539   clib_error_t * error = 0;
540   u32 i;
541
542   f = (char *) format (0, "%s.%d%c", file_name, n, 0);
543   out = fopen (f, "w");
544
545   if (! out)
546     {
547       error = clib_error_return_unix (0, "open `%s'", f);
548       goto done;
549     }
550
551   rtt_test_stream_compute (tm, s);
552   vec_foreach_index (i, s->rtt_histogram)
553     {
554       if (s->rtt_histogram[i] > 0)
555         {
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);
560         }
561     }
562   clib_warning ("wrote `%s'", f);
563
564  done:
565   vec_free (f);
566   fclose (out);
567   return error;
568 }
569
570 static clib_error_t *
571 do_plot (rtt_test_main_t * tm, char * file_name)
572 {
573   rtt_test_stream_t * s;
574   clib_error_t * error = 0;
575   int i, n;
576
577   n = 0;
578   for (i = 0; i < ARRAY_LEN (tm->stream_history); i++)
579     {
580       s = tm->stream_history + i;
581       if (s->n_packets_sent > 0)
582         {
583           error = do_plot_stream (tm, s, file_name, n++);
584           if (error)
585             return error;
586         }
587     }
588
589   pool_foreach (s, tm->stream_pool, ({
590     error = do_plot_stream (tm, s, file_name, n++);
591     if (error)
592       return error;
593   }));
594
595   return error;
596 }
597
598 static clib_error_t *
599 rtt_test_command (vlib_main_t * vm,
600                   unformat_input_t * input,
601                   vlib_cli_command_t * cmd)
602 {
603   rtt_test_main_t * tm = &rtt_test_main;
604   rtt_test_stream_t * s;
605
606   {
607     char * file_name;
608
609     if (unformat (input, "plot %s", &file_name))
610       {
611         clib_error_t * e = do_plot (tm, file_name);
612         vec_free (file_name);
613         return e;
614       }
615   }
616
617   pool_get (tm->stream_pool, s);
618
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)
625     {
626       if (unformat (input, "%U -> %U",
627                     unformat_ip4_address, &s->src_address,
628                     unformat_ip4_address, &s->dst_address))
629         ;
630       else if (unformat (input, "count %f", &s->n_packets_to_send))
631         ;
632       else if (unformat (input, "hist %d", &s->max_n_rx_ack_dts))
633         ;
634       else if (unformat (input, "rate %f", &s->send_rate_bits_per_second))
635         ;
636       else if (unformat (input, "size %d", &s->n_bytes_payload))
637         ;
638       else
639         return clib_error_return (0, "parse error: %U", format_unformat_error, input);
640     }
641
642   if (pool_elts (tm->stream_pool) == 1)
643     vlib_node_set_state (vm, rtt_test_tx_node.index, VLIB_NODE_STATE_POLLING);
644
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;
649
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);
657
658   s->send_rate_packets_per_second = s->send_rate_bits_per_second / (s->n_bytes_per_packet_on_wire * BITS (u8));
659
660   {
661     rtt_test_packet_t * t;
662     int i;
663
664     t = clib_mem_alloc_no_fail (sizeof (t[0]) + s->n_bytes_payload);
665     memset (t, 0, sizeof (t[0]));
666
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;
671     t->ip4.ttl = 64;
672
673     t->ip4.src_address = s->src_address;
674     t->ip4.dst_address = s->dst_address;
675     
676     t->ip4.checksum = ip4_header_checksum (&t->ip4);
677
678     t->rtt.stream_index = s - tm->stream_pool;
679
680     for (i = 0; i < s->n_bytes_payload; i++)
681       t->payload[i] = i;
682
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);
687
688     clib_mem_free (t);
689   }
690
691   {
692     ELOG_TYPE_DECLARE (e) = {
693       .format = "rtt-test: start stream %d",
694       .format_args = "i4",
695     };
696     struct { u32 stream_index; } * ed;
697     ed = ELOG_DATA (&vm->elog_main, e);
698     ed->stream_index = s - tm->stream_pool;
699   }
700
701   return 0;
702 }
703
704 VLIB_CLI_COMMAND (rtt_test_cli_command, static) = {
705   .path = "test rtt",
706   .short_help = "Measure RTT test protocol",
707   .function = rtt_test_command,
708 };
709
710 static u8 * format_rtt_test_stream (u8 * s, va_list * args)
711 {
712   rtt_test_stream_t * t = va_arg (*args, rtt_test_stream_t *);
713   uword indent = format_get_indent (s);
714
715   s = format (s, "%U -> %U",
716               format_ip4_address, &t->src_address,
717               format_ip4_address, &t->dst_address);
718
719   s = format (s, "\n%U  sent %Ld, received: from-past %Ld in-sequence %Ld from-future %Ld",
720               format_white_space, indent,
721               t->n_packets_sent,
722               t->n_packets_received[0], t->n_packets_received[1], t->n_packets_received[2]);
723
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])));
728                
729   rtt_test_stream_compute (&rtt_test_main, t);
730
731   s = format (s, "\n%U  rtt %.4e +- %.4e",
732               format_white_space, indent,
733               t->ave_rtt, t->rms_rtt);
734
735   return s;
736 }
737
738 static clib_error_t *
739 rtt_show_command (vlib_main_t * vm,
740                   unformat_input_t * input,
741                   vlib_cli_command_t * cmd)
742 {
743   rtt_test_main_t * tm = &rtt_test_main;
744   rtt_test_stream_t * s;
745   int i;
746
747   for (i = 0; i < ARRAY_LEN (tm->stream_history); i++)
748     {
749       s = tm->stream_history + i;
750       if (s->n_packets_sent > 0)
751         vlib_cli_output (vm, "%U", format_rtt_test_stream, s);
752     }
753
754   pool_foreach (s, tm->stream_pool, ({
755     vlib_cli_output (vm, "%U", format_rtt_test_stream, s);
756   }));
757
758   return 0;
759 }
760
761 VLIB_CLI_COMMAND (rtt_show_cli_command, static) = {
762   .path = "show rtt",
763   .short_help = "Show RTT measurements",
764   .function = rtt_show_command,
765 };
766
767 static clib_error_t *
768 rtt_test_init (vlib_main_t * vm)
769 {
770   rtt_test_main_t * tm = &rtt_test_main;
771
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);
774
775   {
776     rtt_test_packet_t ack;
777
778     memset (&ack, 0, sizeof (ack));
779
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;
784     ack.ip4.ttl = 64;
785
786     ack.ip4.checksum = ip4_header_checksum (&ack.ip4);
787     tm->ack_packet_template_ip4_checksum = ack.ip4.checksum;
788
789     vlib_packet_template_init (vm, &tm->ack_packet_template,
790                                &ack,
791                                sizeof (ack),
792                                /* alloc chunk size */ VLIB_FRAME_SIZE,
793                                "rtt-test ack");
794   }
795
796   return /* no error */ 0;
797 }
798
799 static VLIB_INIT_FUNCTION (rtt_test_init);
800
801 static clib_error_t *
802 rtt_test_config (vlib_main_t * vm, unformat_input_t * input)
803 {
804   rtt_test_main_t * tm = &rtt_test_main;
805   clib_error_t * error = 0;
806
807   tm->rms_histogram_units = .1;
808   tm->n_encap_bytes = 
809     (14 /* ethernet header */
810      + 8 /* preamble */
811      + 12 /* inter packet gap */
812      + 4 /* crc */);
813   tm->verbose = 1;
814   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
815     {
816       if (unformat (input, "rms-histogram-units %f", &tm->rms_histogram_units))
817         ;
818       else if (unformat (input, "silent"))
819         tm->verbose = 0;
820       else
821         clib_error ("%U", format_unformat_error, input);
822     }
823
824   return error;
825 }
826
827 VLIB_CONFIG_FUNCTION (rtt_test_config, "rtt-test");