else if (unformat (input, "buffer-flags %U",
unformat_vnet_buffer_flags, &s.buffer_flags))
- ;
+ {
+ if (s.buffer_flags & VNET_BUFFER_F_GSO)
+ {
+ if (unformat (input, "gso-size %u", &s.gso_size))
+ ;
+ }
+ }
else if (unformat (input, "buffer-offload-flags %U",
unformat_vnet_buffer_offload_flags, &s.buffer_oflags))
;
goto done;
}
}
+ else if (unformat (line_input, "csum-offload-enabled"))
+ args.flags |= PG_INTERFACE_FLAG_CSUM_OFFLOAD;
else if (unformat (line_input, "hw-addr %U", unformat_ethernet_address,
args.hw_addr.bytes))
args.hw_addr_set = 1;
.short_help =
"create packet-generator interface <interface name>"
" [hw-addr <addr>] [gso-enabled gso-size <size> [coalesce-enabled]]"
- " [mode <ethernet | ip4 | ip6>]",
+ " [csum-offload-enabled] [mode <ethernet | ip4 | ip6>]",
.function = create_pg_if_cmd_fn,
};
.function = delete_pg_if_cmd_fn,
};
+static u8 *
+format_pg_interface_mode (u8 *s, va_list *va)
+{
+ pg_interface_mode_t mode = va_arg (*va, pg_interface_mode_t);
+
+ if (mode == PG_MODE_IP4)
+ s = format (s, "ip4");
+ else if (mode == PG_MODE_IP6)
+ s = format (s, "ip6");
+ else // mode == PG_MODE_ETHERNET
+ s = format (s, "ethernet");
+ return s;
+}
+
+static clib_error_t *
+show_pg_if_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sw_if_index = ~0;
+ pg_interface_details_t pid = { 0 };
+ int rv = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "sw_if_index %d", &sw_if_index))
+ ;
+ else if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
+ &sw_if_index))
+ ;
+ else
+ {
+ return clib_error_create ("unknown input `%U'",
+ format_unformat_error, input);
+ }
+ }
+
+ if (sw_if_index == ~0)
+ return clib_error_return (0,
+ "please specify interface name or sw_if_index");
+
+ rv = pg_interface_details (sw_if_index, &pid);
+ if (rv == VNET_API_ERROR_INVALID_SW_IF_INDEX)
+ return clib_error_return (0, "not a pg interface");
+
+ vlib_cli_output (vm, "Interface: %U", format_vnet_hw_if_index_name, vnm,
+ pid.hw_if_index);
+ vlib_cli_output (vm, " id: %d", pid.id);
+ vlib_cli_output (vm, " mode: %U", format_pg_interface_mode, pid.mode);
+ if ((pid.mode & PG_MODE_ETHERNET) == PG_MODE_ETHERNET)
+ vlib_cli_output (vm, " hw-addr: %U", format_ethernet_address,
+ pid.hw_addr.bytes);
+ vlib_cli_output (vm, " csum_offload_enabled: %d", pid.csum_offload_enabled);
+ vlib_cli_output (vm, " gso_enabled: %d", pid.gso_enabled);
+ if (pid.gso_enabled)
+ vlib_cli_output (vm, " gso_size: %d", pid.gso_size);
+ vlib_cli_output (vm, " coalesce_enabled: %d", pid.coalesce_enabled);
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (show_pg_if_cmd, static) = {
+ .path = "show packet-generator interface",
+ .short_help = "show packet-generator interface {<interface name> | "
+ "sw_if_index <sw_idx>}",
+ .function = show_pg_if_cmd_fn,
+};
+
/* Dummy init function so that we can be linked in. */
static clib_error_t *
pg_cli_init (vlib_main_t * vm)
static_always_inline void
fill_buffer_offload_flags (vlib_main_t *vm, u32 next_index, u32 *buffers,
- u32 n_buffers, u32 buffer_oflags, int gso_enabled,
+ u32 n_buffers, u32 buffer_oflags,
+ int csum_offload_enabled, int gso_enabled,
u32 gso_size)
{
for (int i = 0; i < n_buffers; i++)
(VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
VNET_BUFFER_F_L4_HDR_OFFSET_VALID);
- if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM || gso_enabled)
+ if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM || gso_enabled ||
+ csum_offload_enabled)
oflags |= VNET_BUFFER_OFFLOAD_F_IP_CKSUM;
}
else if (PREDICT_TRUE (ethertype == ETHERNET_TYPE_IP6))
if (l4_proto == IP_PROTOCOL_TCP)
{
- if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM || gso_enabled)
+ if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM || gso_enabled ||
+ csum_offload_enabled)
oflags |= VNET_BUFFER_OFFLOAD_F_TCP_CKSUM;
/* only set GSO flag for chained buffers */
}
else if (l4_proto == IP_PROTOCOL_UDP)
{
- if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)
+ if (buffer_oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM ||
+ csum_offload_enabled)
oflags |= VNET_BUFFER_OFFLOAD_F_UDP_CKSUM;
}
vnet_buffer (b)->feature_arc_index = feature_arc_index;
}
- if (pi->gso_enabled || (s->buffer_flags & VNET_BUFFER_F_OFFLOAD))
+ if (pi->gso_enabled || pi->csum_offload_enabled ||
+ (s->buffer_flags & VNET_BUFFER_F_OFFLOAD))
{
/* we use s->next_index and not next_index on purpose here: we want
* the original node set by the user (typically ethernet-input,
* ip4-input or ip6-input) whereas next_index can be overwritten by
* device-input features */
- fill_buffer_offload_flags (vm, s->next_index, to_next, n_this_frame,
- s->buffer_oflags, pi->gso_enabled,
- pi->gso_size);
+ fill_buffer_offload_flags (
+ vm, s->next_index, to_next, n_this_frame, s->buffer_oflags,
+ pi->csum_offload_enabled, pi->gso_enabled, pi->gso_size);
}
n_trace = vlib_get_trace_count (vm, node);
#include <vnet/ethernet/ethernet.h>
#include <vnet/gso/gro_func.h>
+static_always_inline void
+pg_interface_counter_inline (vlib_main_t *vm, pg_interface_t *pif,
+ uword node_index, u16 n, pg_tx_func_error_t error)
+{
+ vlib_error_count (vm, node_index, error, n);
+ vlib_increment_simple_counter (vnet_main.interface_main.sw_if_counters +
+ VNET_INTERFACE_COUNTER_DROP,
+ vm->thread_index, pif->sw_if_index, n);
+}
+
uword
pg_output (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
{
uword n_buffers = frame->n_vectors;
uword n_left = n_buffers;
u32 to[GRO_TO_VECTOR_SIZE (n_buffers)];
- uword n_to = 0;
+ uword n_to = 0, n_gso_drop = 0, n_csum_offload_drop = 0;
vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
pg_interface_t *pif = pool_elt_at_index (pg->interfaces, rd->dev_instance);
vlib_buffer_t *b = vlib_get_buffer (vm, bi0);
buffers++;
+ if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_GSO))
+ {
+ if (!pif->gso_enabled)
+ {
+ n_gso_drop++;
+ }
+ }
+ else if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_OFFLOAD))
+ {
+ if (!pif->csum_offload_enabled)
+ {
+ n_csum_offload_drop++;
+ }
+ }
+
if (b->flags & VLIB_BUFFER_IS_TRACED)
{
pg_output_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t));
pif->pcap_main.n_packets_to_capture)
pcap_close (&pif->pcap_main);
+ if (n_gso_drop)
+ pg_interface_counter_inline (vm, pif, node->node_index, n_gso_drop,
+ PG_TX_ERROR_GSO_PACKET_DROP);
+ if (n_csum_offload_drop)
+ pg_interface_counter_inline (vm, pif, node->node_index,
+ n_csum_offload_drop,
+ PG_TX_ERROR_CSUM_OFFLOAD_PACKET_DROP);
+
if (PREDICT_FALSE (pif->coalesce_enabled))
{
n_buffers = n_to;
PG_API_MODE_IP6,
};
+enum pg_interface_flags : u32 {
+ PG_API_FLAG_NONE = 0,
+ PG_API_FLAG_CSUM_OFFLOAD = 1, /* enable checksum offload without gso on the interface */
+ PG_API_FLAG_GSO = 2, /* enable gso on the interface */
+ PG_API_FLAG_GRO_COALESCE = 4, /* enable packet coalescing on tx side, provided gso enabled */
+};
+
/** \brief PacketGenerator create interface request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
bool gso_enabled;
u32 gso_size;
};
+
define pg_create_interface_v2
{
+ option deprecated;
+
u32 client_index;
u32 context;
vl_api_interface_index_t interface_id;
vl_api_pg_interface_mode_t mode;
};
+autoendian define pg_create_interface_v3
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t interface_id;
+ vl_api_pg_interface_flags_t pg_flags;
+ u32 gso_size;
+ vl_api_pg_interface_mode_t mode;
+};
+
/** \brief PacketGenerator create interface response
@param context - sender context, to match reply w/ request
@param retval - return value for request
vl_api_interface_index_t sw_if_index;
};
define pg_create_interface_v2_reply
+{
+ option deprecated;
+
+ u32 context;
+ i32 retval;
+ vl_api_interface_index_t sw_if_index;
+};
+
+autoendian define pg_create_interface_v3_reply
{
u32 context;
i32 retval;
/* Buffer flags to set in each packet e.g. l2 valid flags */
u32 buffer_flags;
+ /* GSO size to set in each packet, when buffer is gso-ed */
+ u32 gso_size;
+
/* Buffer offload flags to set in each packet e.g. checksum offload flags */
u32 buffer_oflags;
vec_set_len (s->edit_groups, i);
}
+#define foreach_pg_tx_func_error \
+ _ (GSO_PACKET_DROP, "gso disabled on itf -- gso packet drop") \
+ _ (CSUM_OFFLOAD_PACKET_DROP, \
+ "checksum offload disabled on itf -- csum offload packet drop")
+
+typedef enum
+{
+#define _(f, s) PG_TX_ERROR_##f,
+ foreach_pg_tx_func_error
+#undef _
+ PG_TX_N_ERROR,
+} pg_tx_func_error_t;
+
typedef enum pg_interface_mode_t_
{
PG_MODE_ETHERNET,
u8 coalesce_enabled;
gro_flow_table_t *flow_table;
u8 gso_enabled;
+ u8 csum_offload_enabled;
u32 gso_size;
pcap_main_t pcap_main;
char *pcap_file_name;
mac_address_t *allowed_mcast_macs;
} pg_interface_t;
+typedef struct
+{
+ u32 hw_if_index;
+ u32 id;
+ mac_address_t hw_addr;
+ u8 coalesce_enabled;
+ u8 gso_enabled;
+ u8 csum_offload_enabled;
+ u32 gso_size;
+ pg_interface_mode_t mode;
+} pg_interface_details_t;
+
/* Per VLIB node data. */
typedef struct
{
u32 pg_interface_add_or_get (pg_main_t *pg, pg_interface_args_t *args);
int pg_interface_delete (u32 sw_if_index);
+int pg_interface_details (u32 sw_if_index, pg_interface_details_t *pid);
always_inline pg_node_t *
pg_get_node (uword node_index)
({ rmp->sw_if_index = ntohl (pi->sw_if_index); }));
}
+static void
+vl_api_pg_create_interface_v3_t_handler (vl_api_pg_create_interface_v3_t *mp)
+{
+ vl_api_pg_create_interface_v3_reply_t *rmp;
+ pg_main_t *pg = &pg_main;
+ pg_interface_t *pi;
+ pg_interface_args_t args = { 0 };
+ u32 pg_if_id = ~0;
+ int rv;
+
+ args.mode = (pg_interface_mode_t) mp->mode;
+ if (mp->pg_flags & PG_API_FLAG_CSUM_OFFLOAD)
+ args.flags = PG_INTERFACE_FLAG_CSUM_OFFLOAD;
+ else if (mp->pg_flags & PG_API_FLAG_GSO)
+ {
+ args.flags = PG_INTERFACE_FLAG_GSO;
+ args.gso_size = mp->gso_size;
+ if (mp->pg_flags & PG_API_FLAG_GRO_COALESCE)
+ args.flags |= PG_INTERFACE_FLAG_GRO_COALESCE;
+ }
+ args.if_id = mp->interface_id;
+
+ pg_if_id = pg_interface_add_or_get (pg, &args);
+ pi = pool_elt_at_index (pg->interfaces, pg_if_id);
+
+ rv = args.rv;
+ REPLY_MACRO2_END (VL_API_PG_CREATE_INTERFACE_V3_REPLY,
+ ({ rmp->sw_if_index = pi->sw_if_index; }));
+}
+
static void
vl_api_pg_delete_interface_t_handler (vl_api_pg_delete_interface_t *mp)
{
s->time_last_generate = 0;
}
+static char *pg_tx_func_error_strings[] = {
+#define _(n, s) s,
+ foreach_pg_tx_func_error
+#undef _
+};
+
static u8 *
format_pg_output_trace (u8 * s, va_list * va)
{
.tx_function = pg_output,
.format_device_name = format_pg_interface_name,
.format_tx_trace = format_pg_output_trace,
+ .tx_function_n_errors = PG_TX_N_ERROR,
+ .tx_function_error_strings = pg_tx_func_error_strings,
.admin_up_down_function = pg_interface_admin_up_down,
.mac_addr_add_del_function = pg_add_del_mac_address,
};
VNET_HW_INTERFACE_CLASS (pg_tun_hw_interface_class) = {
.name = "PG-tun",
- //.format_header = format_gre_header_with_length,
- //.unformat_header = unformat_gre_header,
.build_rewrite = NULL,
- //.update_adjacency = gre_update_adj,
.flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
.tx_hash_fn_type = VNET_HASH_FN_TYPE_IP,
};
vnet_main_t *vnm = vnet_get_main ();
pg_interface_t *pi;
vnet_hw_interface_t *hi;
+ vnet_hw_if_caps_change_t cc = { .mask = 0, .val = 0 };
uword *p;
u32 i;
break;
}
hi = vnet_get_hw_interface (vnm, pi->hw_if_index);
+ cc.mask = VNET_HW_IF_CAP_TCP_GSO | VNET_HW_IF_CAP_TX_IP4_CKSUM |
+ VNET_HW_IF_CAP_TX_TCP_CKSUM | VNET_HW_IF_CAP_TX_UDP_CKSUM |
+ VNET_HW_IF_CAP_TX_FIXED_OFFSET;
if (args->flags & PG_INTERFACE_FLAG_GSO)
{
- vnet_hw_if_set_caps (vnm, pi->hw_if_index, VNET_HW_IF_CAP_TCP_GSO);
+ cc.val = VNET_HW_IF_CAP_TCP_GSO | VNET_HW_IF_CAP_TX_IP4_CKSUM |
+ VNET_HW_IF_CAP_TX_TCP_CKSUM | VNET_HW_IF_CAP_TX_UDP_CKSUM |
+ VNET_HW_IF_CAP_TX_FIXED_OFFSET;
+
pi->gso_enabled = 1;
pi->gso_size = args->gso_size;
if (args->flags & PG_INTERFACE_FLAG_GRO_COALESCE)
pg_interface_enable_disable_coalesce (pi, 1, hi->tx_node_index);
}
}
+ else if (args->flags & PG_INTERFACE_FLAG_CSUM_OFFLOAD)
+ {
+ cc.val = VNET_HW_IF_CAP_TX_IP4_CKSUM | VNET_HW_IF_CAP_TX_TCP_CKSUM |
+ VNET_HW_IF_CAP_TX_UDP_CKSUM |
+ VNET_HW_IF_CAP_TX_FIXED_OFFSET;
+ pi->csum_offload_enabled = 1;
+ }
+
+ vnet_hw_if_change_caps (vnm, pi->hw_if_index, &cc);
pi->sw_if_index = hi->sw_if_index;
hash_set (pg->if_index_by_if_id, pi->id, i);
return 0;
}
+int
+pg_interface_details (u32 sw_if_index, pg_interface_details_t *pid)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ pg_main_t *pm = &pg_main;
+ pg_interface_t *pi;
+ vnet_hw_interface_t *hw;
+
+ hw = vnet_get_sup_hw_interface_api_visible_or_null (vnm, sw_if_index);
+ if (hw == NULL || pg_dev_class.index != hw->dev_class_index)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ pi = pool_elt_at_index (pm->interfaces, hw->dev_instance);
+
+ pid->hw_if_index = pi->hw_if_index;
+ pid->id = pi->id;
+ pid->mode = pi->mode;
+ mac_address_from_bytes (&pid->hw_addr, pi->hw_addr.bytes);
+ pid->csum_offload_enabled = pi->csum_offload_enabled;
+ pid->gso_enabled = pi->gso_enabled;
+ pid->gso_size = pi->gso_size;
+ pid->coalesce_enabled = pi->coalesce_enabled;
+
+ return 0;
+}
+
static void
do_edit (pg_stream_t * stream,
pg_edit_group_t * g, pg_edit_t * e, uword want_commit)
{
vlib_main_t *vm = vlib_get_main ();
pg_stream_t *s;
+ pg_interface_flags_t flags = 0;
+ u32 gso_size = 0;
uword *p;
if (!pg->stream_index_by_name)
vec_resize (s->buffer_indices, n);
}
+ if (s->buffer_flags & VNET_BUFFER_F_GSO)
+ {
+ flags = PG_INTERFACE_FLAG_GSO;
+ gso_size = s->gso_size;
+ }
+ else if (s->buffer_flags & VNET_BUFFER_F_OFFLOAD)
+ {
+ flags = PG_INTERFACE_FLAG_CSUM_OFFLOAD;
+ }
+
pg_interface_args_t args = {
.if_id = s->if_id,
.mode = PG_MODE_ETHERNET,
- .flags = 0, /* gso_enabled and coalesce_enabled */
- .gso_size = 0, /* gso_size */
- .hw_addr_set = 0, /* mac address set */
+ .flags =
+ flags, /* csum_offload_enabled, gso_enabled and/or coalesce_enabled */
+ .gso_size = gso_size, /* gso_size */
+ .hw_addr_set = 0, /* mac address set */
};
/* Find an interface to use. */
cls._pcaps = []
@classmethod
- def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
+ def create_pg_interfaces_internal(
+ cls, interfaces, csum_offload=0, gso=0, gso_size=0, mode=None
+ ):
"""
Create packet-generator interfaces.
"""
result = []
for i in interfaces:
- intf = VppPGInterface(cls, i, gso, gso_size, mode)
+ intf = VppPGInterface(cls, i, csum_offload, gso, gso_size, mode)
setattr(cls, intf.name, intf)
result.append(intf)
cls.pg_interfaces = result
return result
@classmethod
- def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
+ def create_pg_ip4_interfaces(cls, interfaces, csum_offload=0, gso=0, gso_size=0):
if not hasattr(cls, "vpp"):
cls.pg_interfaces = []
return cls.pg_interfaces
pgmode = VppEnum.vl_api_pg_interface_mode_t
return cls.create_pg_interfaces_internal(
- interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
+ interfaces, csum_offload, gso, gso_size, pgmode.PG_API_MODE_IP4
)
@classmethod
- def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
+ def create_pg_ip6_interfaces(cls, interfaces, csum_offload=0, gso=0, gso_size=0):
if not hasattr(cls, "vpp"):
cls.pg_interfaces = []
return cls.pg_interfaces
pgmode = VppEnum.vl_api_pg_interface_mode_t
return cls.create_pg_interfaces_internal(
- interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
+ interfaces, csum_offload, gso, gso_size, pgmode.PG_API_MODE_IP6
)
@classmethod
- def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
+ def create_pg_interfaces(cls, interfaces, csum_offload=0, gso=0, gso_size=0):
if not hasattr(cls, "vpp"):
cls.pg_interfaces = []
return cls.pg_interfaces
pgmode = VppEnum.vl_api_pg_interface_mode_t
return cls.create_pg_interfaces_internal(
- interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
+ interfaces, csum_offload, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
)
@classmethod
- def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
+ def create_pg_ethernet_interfaces(
+ cls, interfaces, csum_offload=0, gso=0, gso_size=0
+ ):
if not hasattr(cls, "vpp"):
cls.pg_interfaces = []
return cls.pg_interfaces
pgmode = VppEnum.vl_api_pg_interface_mode_t
return cls.create_pg_interfaces_internal(
- interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
+ interfaces, csum_offload, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
)
@classmethod
def setUpClass(self):
super(TestGSO, self).setUpClass()
res = self.create_pg_interfaces(range(2))
- res_gso1 = self.create_pg_interfaces(range(2, 3), 1, 1460)
- res_gso2 = self.create_pg_interfaces(range(3, 4), 1, 1440)
- self.pg_interfaces = self.create_pg_interfaces(range(4, 5), 1, 8940)
+ res_gso1 = self.create_pg_interfaces(
+ range(2, 3), csum_offload=0, gso=1, gso_size=1460
+ )
+ res_gso2 = self.create_pg_interfaces(
+ range(3, 4), csum_offload=0, gso=1, gso_size=1440
+ )
+ self.pg_interfaces = self.create_pg_interfaces(
+ range(4, 5), csum_offload=0, gso=1, gso_size=8940
+ )
self.pg_interfaces.append(res[0])
self.pg_interfaces.append(res[1])
self.pg_interfaces.append(res_gso1[0])
from scapy.packet import Raw
from scapy.layers.l2 import Ether
-from scapy.layers.inet import IP, UDP
+from scapy.layers.inet import IP, UDP, TCP
from scapy.layers.inet6 import IPv6
from framework import VppTestCase
from asfframework import VppTestRunner
-class TestPgTun(VppTestCase):
+class TestPg(VppTestCase):
"""PG Test Case"""
+ def __init__(self, *args):
+ VppTestCase.__init__(self, *args)
+
+ @classmethod
+ def setUpClass(self):
+ super(TestPg, self).setUpClass()
+
+ @classmethod
+ def tearDownClass(self):
+ super(TestPg, self).tearDownClass()
+
def setUp(self):
- super(TestPgTun, self).setUp()
+ super(TestPg, self).setUp()
+ # create pg interfaces
- # create 3 pg interfaces - one each ethernet, ip4-tun, ip6-tun.
+ # ethernet
self.create_pg_interfaces(range(0, 1))
+ # ip4-tun
self.pg_interfaces += self.create_pg_ip4_interfaces(range(1, 2))
+ # ip6-tun
self.pg_interfaces += self.create_pg_ip6_interfaces(range(2, 3))
+ # ethernet with checksum offload
+ self.pg_interfaces += self.create_pg_interfaces(range(3, 4), 1)
+ # ethernet with gso offload
+ self.pg_interfaces += self.create_pg_interfaces(range(4, 5), 0, 1, 1458)
for i in self.pg_interfaces:
i.admin_up()
- for i in [self.pg0, self.pg1]:
+ for i in [self.pg0, self.pg1, self.pg3, self.pg4]:
i.config_ip4()
for i in [self.pg0, self.pg2]:
i.config_ip6()
self.pg0.resolve_arp()
+ self.pg3.resolve_arp()
+ self.pg4.resolve_arp()
self.pg0.resolve_ndp()
def tearDown(self):
- for i in self.pg_interfaces:
+ super(TestPg, self).tearDown()
+ for i in [self.pg0, self.pg1, self.pg3, self.pg4]:
i.unconfig_ip4()
+ for i in [self.pg0, self.pg2]:
+ i.unconfig_ip6()
+ for i in self.pg_interfaces:
i.admin_down()
- i.remove_vpp_config()
- super(TestPgTun, self).tearDown()
def test_pg_tun(self):
"""IP[46] Tunnel Mode PG"""
self.assertFalse(rx.haslayer(Ether))
self.assertEqual(rx[IPv6].dst, self.pg2.remote_ip6)
+ def test_pg_offload(self):
+ """PG Interface Offload"""
+
+ N_PKTS = 31
+
+ p03 = (
+ Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac)
+ / IP(src=self.pg3.remote_ip4, dst=self.pg0.remote_ip4)
+ / UDP(sport=1234, dport=1234, chksum=0)
+ / Raw("0" * 48)
+ )
+
+ rxs = self.send_and_expect(self.pg3, p03 * N_PKTS, self.pg0)
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg0.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
+ self.assert_ip_checksum_valid(rx)
+ self.assert_udp_checksum_valid(rx, ignore_zero_checksum=False)
+ self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
+
+ p04 = (
+ Ether(dst=self.pg4.local_mac, src=self.pg4.remote_mac)
+ / IP(src=self.pg4.remote_ip4, dst=self.pg3.remote_ip4, flags="DF")
+ / TCP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 65200)
+ )
+
+ rxs = self.send_and_expect(self.pg4, p04 * N_PKTS, self.pg3)
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg3.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg3.remote_mac)
+ self.assertEqual(rx[IP].dst, self.pg3.remote_ip4)
+
+ r = self.vapi.cli_return_response("show errors")
+ self.assertTrue(r.retval == 0)
+ self.assertTrue(hasattr(r, "reply"))
+ rv = r.reply
+ outcome = rv.find(
+ "31 pg3-tx gso disabled on itf -- gso packet error"
+ )
+ self.assertNotEqual(outcome, -1)
+
if __name__ == "__main__":
unittest.main(testRunner=VppTestRunner)
"""packet-generator interface index assigned by VPP"""
return self._pg_index
+ @property
+ def csum_offload_enabled(self):
+ """csum offload enabled on packet-generator interface"""
+ if self._csum_offload_enabled == 0:
+ return "csum-offload-disabled"
+ return "csum-offload-enabled"
+
@property
def gso_enabled(self):
"""gso enabled on packet-generator interface"""
self._out_history_counter += 1
return v
- def __init__(self, test, pg_index, gso, gso_size, mode):
+ def __init__(self, test, pg_index, csum_offload, gso, gso_size, mode):
"""Create VPP packet-generator interface"""
super().__init__(test)
- r = test.vapi.pg_create_interface_v2(pg_index, gso, gso_size, mode)
+ pg_flags = VppEnum.vl_api_pg_interface_flags_t.PG_API_FLAG_NONE
+ pgflags = VppEnum.vl_api_pg_interface_flags_t
+ if csum_offload:
+ pg_flags = pgflags.PG_API_FLAG_CSUM_OFFLOAD
+ if gso:
+ pg_flags = pgflags.PG_API_FLAG_GSO
+ r = test.vapi.pg_create_interface_v3(pg_index, pg_flags, gso_size, mode)
self.set_sw_if_index(r.sw_if_index)
self._in_history_counter = 0
self._out_history_counter = 0
self._out_assert_counter = 0
self._pg_index = pg_index
+ self._csum_offload_enabled = csum_offload
self._gso_enabled = gso
self._gso_size = gso_size
self._coalesce_enabled = 0