From: Dave Barach Date: Thu, 6 Dec 2018 15:20:14 +0000 (-0500) Subject: nsim: add packet loss simulation, docs X-Git-Tag: v19.04-rc0~222 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=10c5ff1 nsim: add packet loss simulation, docs Change-Id: Ic9747541aad8148ebf7d520b525b99c4cc3961f3 Signed-off-by: Dave Barach --- diff --git a/docs/usecases/index.rst b/docs/usecases/index.rst index 735947cd994..3dabd78ca06 100644 --- a/docs/usecases/index.rst +++ b/docs/usecases/index.rst @@ -14,5 +14,5 @@ extensive list, but should give a sampling of the many features contained in FD. vhost/index.rst homegateway contiv/index.rst - + networksim diff --git a/docs/usecases/networksim.md b/docs/usecases/networksim.md new file mode 100644 index 00000000000..817ddf82a29 --- /dev/null +++ b/docs/usecases/networksim.md @@ -0,0 +1,90 @@ +Network Simulator Plugin +======================== + +Vpp includes a fairly capable network simulator plugin, which can +simulate real-world round-trip times and a configurable network packet +loss rate. It's perfect for evaluating the performance of a TCP stack +under specified delay/bandwidth/loss conditions. + +The "nsim" plugin cross-connects two physical interfaces at layer 2, +introducing the specified delay and network loss +parameters. Reconfiguration on the fly is OK, with the proviso that +packets held in the network simulator scheduling wheel will be lost. + +Configuration +------------- + +Configuration by debug CLI is simple. First, specify the simulator +configuration: unidirectional delay (half of the desired RTT), the +link bandwidth, and the expected average packet size. These parameters +allow the network simulator allocate the right amount of buffering to +produce the requested delay/bandwidth product. + +``` + set nsim delay 25.0 ms bandwidth 10 gbit packet-size 128 +``` + +To simulate network packet drops, add either "packets-per-drop " or +"drop-fraction [0.0 ... 1.0]" parameters: + +``` + set nsim delay 25.0 ms bandwidth 10 gbit packet-size 128 packets-per-drop 10000 +``` +Remember to configure the layer-2 cross-connect: + +``` + nsim enable-disable +``` + +Packet Generator Configuration +------------------------------ + +Here's a unit-test configuration for the vpp packet generator: + +``` + loop cre + set int ip address loop0 11.22.33.1/24 + set int state loop0 up + + loop cre + set int ip address loop1 11.22.34.1/24 + set int state loop1 up + + set nsim delay 1.0 ms bandwidth 10 gbit packet-size 128 packets-per-drop 1000 + nsim enable-disable loop0 loop1 + + packet-generator new { + name s0 + limit 10000 + size 128-128 + interface loop0 + node ethernet-input + data { IP4: 1.2.3 -> 4.5.6 + UDP: 11.22.33.44 -> 11.22.34.44 + UDP: 1234 -> 2345 + incrementing 114 + } + } +``` + +For extra realism, the network simulator drops any specific packet +with the specified probability. In this example, we see that slight +variation from run to run occurs as it should. + +``` + DBGvpp# pa en + DBGvpp# sh err + Count Node Reason + 9991 nsim Packets buffered + 9 nsim Network loss simulation drop packets + 9991 ethernet-input l3 mac mismatch + + DBGvpp# clear err + DBGvpp# pa en + DBGvpp# sh err + sh err + Count Node Reason + 9993 nsim Packets buffered + 7 nsim Network loss simulation drop packets + 9993 ethernet-input l3 mac mismatch +``` diff --git a/src/plugins/nsim/node.c b/src/plugins/nsim/node.c index d6413778bf3..25112abe299 100644 --- a/src/plugins/nsim/node.c +++ b/src/plugins/nsim/node.c @@ -25,6 +25,7 @@ typedef struct f64 expires; u32 tx_sw_if_index; int is_drop; + int is_lost; } nsim_trace_t; #ifndef CLIB_MARCH_VARIANT @@ -38,7 +39,8 @@ format_nsim_trace (u8 * s, va_list * args) nsim_trace_t *t = va_arg (*args, nsim_trace_t *); if (t->is_drop) - s = format (s, "NSIM: ring drop"); + s = format (s, "NSIM: dropped, %s", t->is_lost ? + "simulated network loss" : "no space in ring"); else s = format (s, "NSIM: tx time %.6f sw_if_index %d", t->expires, t->tx_sw_if_index); @@ -51,7 +53,8 @@ vlib_node_registration_t nsim_node; #define foreach_nsim_error \ _(BUFFERED, "Packets buffered") \ -_(DROPPED, "Packets dropped due to lack of space") +_(DROPPED, "Packets dropped due to lack of space") \ +_(LOSS, "Network loss simulation drop packets") typedef enum { @@ -90,6 +93,7 @@ nsim_inline (vlib_main_t * vm, int is_drop0; u32 no_error = node->errors[NSIM_ERROR_BUFFERED]; u32 no_buffer_error = node->errors[NSIM_ERROR_DROPPED]; + u32 loss_error = node->errors[NSIM_ERROR_LOSS]; nsim_wheel_entry_t *ep = 0; ASSERT (wp); @@ -109,6 +113,19 @@ nsim_inline (vlib_main_t * vm, is_drop0 = 0; if (PREDICT_TRUE (wp->cursize < wp->wheel_size)) { + if (PREDICT_FALSE (nsm->drop_fraction != 0.0)) + { + /* Get a random number on the closed interval [0,1] */ + f64 rnd = random_f64 (&nsm->seed); + /* Drop the pkt? */ + if (rnd <= nsm->drop_fraction) + { + b[0]->error = loss_error; + is_drop0 = 1; + goto do_trace; + } + } + ep = wp->entries + wp->tail; wp->tail++; if (wp->tail == wp->wheel_size) @@ -130,6 +147,7 @@ nsim_inline (vlib_main_t * vm, is_drop0 = 1; } + do_trace: if (is_trace) { if (b[0]->flags & VLIB_BUFFER_IS_TRACED) @@ -137,6 +155,7 @@ nsim_inline (vlib_main_t * vm, nsim_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t)); t->expires = expires; t->is_drop = is_drop0; + t->is_lost = b[0]->error == loss_error; t->tx_sw_if_index = (is_drop0 == 0) ? ep->tx_sw_if_index : 0; } } diff --git a/src/plugins/nsim/nsim.api b/src/plugins/nsim/nsim.api index 89174268aee..7bb84bafacd 100644 --- a/src/plugins/nsim/nsim.api +++ b/src/plugins/nsim/nsim.api @@ -3,7 +3,7 @@ * @brief VPP control-plane API messages for the network delay simulator */ -option version = "1.0.0"; +option version = "1.1.0"; /** \brief enable / disable the network delay simulation cross-connect @param client_index - opaque cookie to identify the sender @@ -47,4 +47,5 @@ autoreply define nsim_configure u32 delay_in_usec; u32 average_packet_size; u64 bandwidth_in_bits_per_second; + u32 packets_per_drop; }; diff --git a/src/plugins/nsim/nsim.c b/src/plugins/nsim/nsim.c index ec79070b2e7..ce3396c5845 100644 --- a/src/plugins/nsim/nsim.c +++ b/src/plugins/nsim/nsim.c @@ -118,7 +118,8 @@ nsim_enable_disable (nsim_main_t * nsm, u32 sw_if_index0, } static int -nsim_configure (nsim_main_t * nsm, f64 bandwidth, f64 delay, f64 packet_size) +nsim_configure (nsim_main_t * nsm, f64 bandwidth, f64 delay, f64 packet_size, + f64 drop_fraction) { u64 total_buffer_size_in_bytes, per_worker_buffer_size; u64 wheel_slots_per_worker; @@ -148,6 +149,7 @@ nsim_configure (nsim_main_t * nsm, f64 bandwidth, f64 delay, f64 packet_size) } nsm->delay = delay; + nsm->drop_fraction = drop_fraction; /* delay in seconds, bandwidth in bits/sec */ total_buffer_size_in_bytes = (u32) ((delay * bandwidth) / 8.0) + 0.5; @@ -318,14 +320,21 @@ vl_api_nsim_configure_t_handler (vl_api_nsim_configure_t * mp) { vl_api_nsim_configure_reply_t *rmp; nsim_main_t *nsm = &nsim_main; - f64 delay, bandwidth, packet_size; + f64 delay, bandwidth, packet_size, drop_fraction; + u32 packets_per_drop; int rv; delay = ((f64) (ntohl (mp->delay_in_usec))) * 1e-6; bandwidth = (f64) (clib_net_to_host_u64 (mp->bandwidth_in_bits_per_second)); packet_size = (f64) (ntohl (mp->average_packet_size)); - rv = nsim_configure (nsm, bandwidth, delay, packet_size); + packets_per_drop = ntohl (mp->packets_per_drop); + if (packets_per_drop > 0) + drop_fraction = 1.0 / (f64) (packets_per_drop); + else + drop_fraction = 0.0; + + rv = nsim_configure (nsm, bandwidth, delay, packet_size, drop_fraction); REPLY_MACRO (VL_API_NSIM_CONFIGURE_REPLY); } @@ -447,6 +456,8 @@ set_nsim_command_fn (vlib_main_t * vm, nsim_main_t *nsm = &nsim_main; f64 delay, bandwidth; f64 packet_size = 1500.0; + f64 drop_fraction = 0.0; + u32 packets_per_drop; u32 num_workers = vlib_num_workers (); int rv; @@ -459,11 +470,22 @@ set_nsim_command_fn (vlib_main_t * vm, ; else if (unformat (input, "packet-size %f", &packet_size)) ; + else if (unformat (input, "packets-per-drop %d", &packets_per_drop)) + { + if (packets_per_drop > 0) + drop_fraction = 1.0 / ((f64) packets_per_drop); + } + else if (unformat (input, "drop-fraction %f", &drop_fraction)) + { + if (drop_fraction < 0.0 || drop_fraction > 1.0) + return clib_error_return + (0, "drop fraction must be between zero and 1"); + } else break; } - rv = nsim_configure (nsm, bandwidth, delay, packet_size); + rv = nsim_configure (nsm, bandwidth, delay, packet_size, drop_fraction); switch (rv) { @@ -485,6 +507,10 @@ set_nsim_command_fn (vlib_main_t * vm, vlib_cli_output (vm, "Configured link delay %.2f ms, %.2f ms round-trip", nsm->delay * 1e3, 2.0 * nsm->delay * 1e3); + if (nsm->drop_fraction > 0.0) + vlib_cli_output (vm, "... simulating a network drop fraction of %.5f", + nsm->drop_fraction); + if (num_workers) vlib_cli_output (vm, "Sim uses %llu bytes per thread, %llu bytes total", @@ -548,6 +574,10 @@ show_nsim_command_fn (vlib_main_t * vm, "...inserting link delay of %.2f ms, %.2f ms round-trip", nsm->delay * 1e3, 2.0 * nsm->delay * 1e3); + if (nsm->drop_fraction > 0.0) + vlib_cli_output (vm, "... simulating a network drop fraction of %.5f", + nsm->drop_fraction); + if (verbose) { diff --git a/src/plugins/nsim/nsim.h b/src/plugins/nsim/nsim.h index d0b5ed32fee..c5264ecb244 100644 --- a/src/plugins/nsim/nsim.h +++ b/src/plugins/nsim/nsim.h @@ -54,6 +54,8 @@ typedef struct /* Two interfaces, cross-connected with delay */ u32 sw_if_index0, sw_if_index1; u32 output_next_index0, output_next_index1; + /* Random seed for loss-rate simulation */ + u32 seed; /* Per-thread buffer / scheduler wheels */ nsim_wheel_t **wheel_by_thread; @@ -63,6 +65,7 @@ typedef struct f64 delay; f64 bandwidth; f64 packet_size; + f64 drop_fraction; u64 mmap_size; diff --git a/src/plugins/nsim/nsim_input.c b/src/plugins/nsim/nsim_input.c index 66bd8d10954..2e328a58bf3 100644 --- a/src/plugins/nsim/nsim_input.c +++ b/src/plugins/nsim/nsim_input.c @@ -76,6 +76,7 @@ nsim_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, u32 my_thread_index = vm->thread_index; u32 *my_buffer_cache = nsm->buffer_indices_by_thread[my_thread_index]; nsim_wheel_t *wp = nsm->wheel_by_thread[my_thread_index]; + u32 n_trace = vlib_get_trace_count (vm, node); f64 now = vlib_time_now (vm); uword n_rx_packets = 0; vlib_buffer_t *b0; @@ -153,6 +154,18 @@ nsim_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, b0->current_data = 0; b0->current_length = ep->current_length; + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + + if (PREDICT_FALSE (n_trace)) + { + nsim_tx_trace_t *t0; + vlib_trace_buffer (vm, node, next_index, b0, + 0 /* follow_chain */ ); + t0 = vlib_add_trace (vm, node, b0, sizeof (*t0)); + t0->expired = ep->tx_time; + t0->tx_sw_if_index = ep->tx_sw_if_index; + } + /* Copy data from the ring */ clib_memcpy_fast (b0->data, ep->data, ep->current_length); b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID; diff --git a/src/plugins/nsim/nsim_test.c b/src/plugins/nsim/nsim_test.c index 0e2a26af55b..7123703fd42 100644 --- a/src/plugins/nsim/nsim_test.c +++ b/src/plugins/nsim/nsim_test.c @@ -182,6 +182,7 @@ api_nsim_configure (vat_main_t * vam) f64 delay = 0.0, bandwidth = 0.0; f64 packet_size = 1500.0; u32 num_workers = vlib_num_workers (); + u32 packets_per_drop = 0; int ret; while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) @@ -192,6 +193,8 @@ api_nsim_configure (vat_main_t * vam) ; else if (unformat (i, "packet-size %f", &packet_size)) ; + else if (unformat (i, "packets-per-drop %u", &packets_per_drop)) + ; else break; } @@ -211,6 +214,7 @@ api_nsim_configure (vat_main_t * vam) mp->bandwidth_in_bits_per_second = (u64) (bandwidth); mp->bandwidth_in_bits_per_second = clib_host_to_net_u64 (mp->bandwidth_in_bits_per_second); + mp->packets_per_drop = ntohl (packets_per_drop); /* send it... */ S (mp); @@ -227,7 +231,8 @@ api_nsim_configure (vat_main_t * vam) #define foreach_vpe_api_msg \ _(nsim_enable_disable, \ "[ | sw_if_index ] [ | sw_if_index ] [disable]") \ -_(nsim_configure, "delay