pg: add support for checksum offload 98/42598/14
authorMohsin Kazmi <[email protected]>
Wed, 2 Jul 2025 10:48:11 +0000 (10:48 +0000)
committerBenoit Ganne <[email protected]>
Mon, 4 Aug 2025 16:06:42 +0000 (16:06 +0000)
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 <[email protected]>
src/vnet/pg/cli.c
src/vnet/pg/input.c
src/vnet/pg/output.c
src/vnet/pg/pg.api
src/vnet/pg/pg.h
src/vnet/pg/pg_api.c
src/vnet/pg/stream.c
test/framework.py
test/test_gso.py
test/test_pg.py
test/vpp_pg_interface.py

index 1478243..da10884 100644 (file)
@@ -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 <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,
 };
 
@@ -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 {<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)
index 4f89c73..18ad6db 100644 (file)
@@ -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);
index 5287f3e..f9df00e 100644 (file)
 #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)
 {
@@ -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;
index 7c6fdcc..cea3760 100644 (file)
@@ -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;
index 7c5d698..172c279 100644 (file)
@@ -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)
index 6895353..997a56d 100644 (file)
@@ -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)
 {
index 31a41e8..f843036 100644 (file)
@@ -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. */
index 05d1018..6879115 100644 (file)
@@ -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
index 63b42c9..39a08bc 100644 (file)
@@ -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])
index 14e149b..21ae2c1 100644 (file)
@@ -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)
index cd99818..1c82eba 100644 (file)
@@ -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