nsim: add packet loss simulation, docs
Change-Id: Ic9747541aad8148ebf7d520b525b99c4cc3961f3 Signed-off-by: Dave Barach <dave@barachs.net>
This commit is contained in:

committed by
Florin Coras

parent
995ff06fb7
commit
10c5ff143a
@ -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
|
||||
|
||||
|
90
docs/usecases/networksim.md
Normal file
90
docs/usecases/networksim.md
Normal 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
|
||||
```
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user