From 513c6bf8f1e53fde08a6bcbaf82d09f1fc48d9aa Mon Sep 17 00:00:00 2001 From: Mohsin Kazmi Date: Wed, 2 Jul 2025 10:48:11 +0000 Subject: [PATCH] pg: add support for checksum offload Type: improvement This patch adds support for checksum offload. There has been also added show packet-generator interface cli. Change-Id: I55462df45ea54b577c110e1cc4e3512d70bcfa90 Signed-off-by: Mohsin Kazmi --- src/vnet/pg/cli.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++-- src/vnet/pg/input.c | 21 ++++++++----- src/vnet/pg/output.c | 35 ++++++++++++++++++++- src/vnet/pg/pg.api | 29 ++++++++++++++++++ src/vnet/pg/pg.h | 30 ++++++++++++++++++ src/vnet/pg/pg_api.c | 30 ++++++++++++++++++ src/vnet/pg/stream.c | 74 +++++++++++++++++++++++++++++++++++++++----- test/framework.py | 24 +++++++++------ test/test_gso.py | 12 ++++++-- test/test_pg.py | 80 +++++++++++++++++++++++++++++++++++++++++++----- test/vpp_pg_interface.py | 18 +++++++++-- 11 files changed, 392 insertions(+), 41 deletions(-) diff --git a/src/vnet/pg/cli.c b/src/vnet/pg/cli.c index 147824394a5..da108845745 100644 --- a/src/vnet/pg/cli.c +++ b/src/vnet/pg/cli.c @@ -382,7 +382,13 @@ new_stream (vlib_main_t * vm, 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)) ; @@ -694,6 +700,8 @@ create_pg_if_cmd_fn (vlib_main_t * vm, 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; @@ -722,7 +730,7 @@ VLIB_CLI_COMMAND (create_pg_if_cmd, static) = { .short_help = "create packet-generator interface " " [hw-addr ] [gso-enabled gso-size [coalesce-enabled]]" - " [mode ]", + " [csum-offload-enabled] [mode ]", .function = create_pg_if_cmd_fn, }; @@ -773,6 +781,74 @@ VLIB_CLI_COMMAND (delete_pg_if_cmd, static) = { .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 { | " + "sw_if_index }", + .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) diff --git a/src/vnet/pg/input.c b/src/vnet/pg/input.c index 4f89c73a932..18ad6db3aea 100644 --- a/src/vnet/pg/input.c +++ b/src/vnet/pg/input.c @@ -1548,7 +1548,8 @@ pg_input_trace (pg_main_t * pg, 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++) @@ -1605,7 +1606,8 @@ fill_buffer_offload_flags (vlib_main_t *vm, u32 next_index, u32 *buffers, (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)) @@ -1622,7 +1624,8 @@ fill_buffer_offload_flags (vlib_main_t *vm, u32 next_index, u32 *buffers, 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 */ @@ -1637,7 +1640,8 @@ fill_buffer_offload_flags (vlib_main_t *vm, u32 next_index, u32 *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; } @@ -1746,15 +1750,16 @@ pg_generate_packets (vlib_node_runtime_t * node, 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); diff --git a/src/vnet/pg/output.c b/src/vnet/pg/output.c index 5287f3eb8e9..f9df00e803a 100644 --- a/src/vnet/pg/output.c +++ b/src/vnet/pg/output.c @@ -44,6 +44,16 @@ #include #include +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) { @@ -52,7 +62,7 @@ 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); @@ -74,6 +84,21 @@ pg_output (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) 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)); @@ -101,6 +126,14 @@ pg_output (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) 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; diff --git a/src/vnet/pg/pg.api b/src/vnet/pg/pg.api index 7c6fdcc97cf..cea37603e25 100644 --- a/src/vnet/pg/pg.api +++ b/src/vnet/pg/pg.api @@ -29,6 +29,13 @@ enum pg_interface_mode : u8 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 @@ -46,8 +53,11 @@ define pg_create_interface 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; @@ -56,6 +66,16 @@ define pg_create_interface_v2 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 @@ -69,6 +89,15 @@ define pg_create_interface_reply 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; diff --git a/src/vnet/pg/pg.h b/src/vnet/pg/pg.h index 7c5d698349b..172c279a972 100644 --- a/src/vnet/pg/pg.h +++ b/src/vnet/pg/pg.h @@ -128,6 +128,9 @@ typedef struct pg_stream_t /* 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; @@ -332,6 +335,19 @@ pg_free_edit_group (pg_stream_t * s) 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, @@ -387,6 +403,7 @@ typedef struct 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; @@ -395,6 +412,18 @@ typedef struct 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 { @@ -453,6 +482,7 @@ void pg_interface_enable_disable_coalesce (pg_interface_t * pi, u8 enable, 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) diff --git a/src/vnet/pg/pg_api.c b/src/vnet/pg/pg_api.c index 68953533b07..997a56d1d1a 100644 --- a/src/vnet/pg/pg_api.c +++ b/src/vnet/pg/pg_api.c @@ -89,6 +89,36 @@ vl_api_pg_create_interface_v2_t_handler (vl_api_pg_create_interface_v2_t *mp) ({ 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) { diff --git a/src/vnet/pg/stream.c b/src/vnet/pg/stream.c index 31a41e8cc88..f843036fdbe 100644 --- a/src/vnet/pg/stream.c +++ b/src/vnet/pg/stream.c @@ -94,6 +94,12 @@ pg_stream_enable_disable (pg_main_t * pg, pg_stream_t * s, int want_enabled) 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) { @@ -183,6 +189,8 @@ VNET_DEVICE_CLASS (pg_dev_class) = { .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, }; @@ -243,10 +251,7 @@ format_pg_tun_tx_trace (u8 *s, va_list *args) 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, }; @@ -257,6 +262,7 @@ pg_interface_add_or_get (pg_main_t *pg, pg_interface_args_t *args) 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; @@ -295,9 +301,15 @@ pg_interface_add_or_get (pg_main_t *pg, pg_interface_args_t *args) 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) @@ -305,6 +317,15 @@ pg_interface_add_or_get (pg_main_t *pg, pg_interface_args_t *args) 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); @@ -367,6 +388,32 @@ pg_interface_delete (u32 sw_if_index) 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) @@ -525,6 +572,8 @@ pg_stream_add (pg_main_t * pg, pg_stream_t * s_init) { 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) @@ -583,12 +632,23 @@ pg_stream_add (pg_main_t * pg, pg_stream_t * s_init) 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. */ diff --git a/test/framework.py b/test/framework.py index 05d1018567e..68791152403 100644 --- a/test/framework.py +++ b/test/framework.py @@ -158,7 +158,9 @@ class VppTestCase(VppAsfTestCase): 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. @@ -168,50 +170,52 @@ class VppTestCase(VppAsfTestCase): """ 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 diff --git a/test/test_gso.py b/test/test_gso.py index 63b42c95309..39a08bc2d74 100644 --- a/test/test_gso.py +++ b/test/test_gso.py @@ -49,9 +49,15 @@ class TestGSO(VppTestCase): 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]) diff --git a/test/test_pg.py b/test/test_pg.py index 14e149b5bcf..21ae2c17b91 100644 --- a/test/test_pg.py +++ b/test/test_pg.py @@ -4,42 +4,64 @@ import unittest 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""" @@ -102,6 +124,48 @@ class TestPgTun(VppTestCase): 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) diff --git a/test/vpp_pg_interface.py b/test/vpp_pg_interface.py index cd99818caa4..1c82ebae8e7 100644 --- a/test/vpp_pg_interface.py +++ b/test/vpp_pg_interface.py @@ -57,6 +57,13 @@ class VppPGInterface(VppInterface): """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""" @@ -125,17 +132,24 @@ class VppPGInterface(VppInterface): 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 -- 2.16.6