nsim: add packet loss simulation, docs

Change-Id: Ic9747541aad8148ebf7d520b525b99c4cc3961f3
Signed-off-by: Dave Barach <dave@barachs.net>
This commit is contained in:
Dave Barach
2018-12-06 10:20:14 -05:00
committed by Florin Coras
parent 995ff06fb7
commit 10c5ff143a
8 changed files with 170 additions and 9 deletions

View File

@ -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

View File

@ -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 <nnnnn>" 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 <interface-1> <interface-2>
```
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
```

View File

@ -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;
}
}

View File

@ -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;
};

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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, \
"[<intfc0> | sw_if_index <swif0>] [<intfc1> | sw_if_index <swif1>] [disable]") \
_(nsim_configure, "delay <time> bandwidth <bw> [packet-size <nn>]")
_(nsim_configure, "delay <time> bandwidth <bw> [packet-size <nn>]" \
"[packets-per-drop <nnnn>]")
static void
nsim_api_hookup (vat_main_t * vam)