udp: support for disabling tx csum

Type: feature

Signed-off-by: Florin Coras <fcoras@cisco.com>
Change-Id: I84843eb3a0a66c64cd46536624633e0dae1c4681
diff --git a/src/vnet/udp/udp.c b/src/vnet/udp/udp.c
index c919c95..7851da8 100644
--- a/src/vnet/udp/udp.c
+++ b/src/vnet/udp/udp.c
@@ -194,6 +194,8 @@
   else
     listener->c_flags |= TRANSPORT_CONNECTION_F_CLESS;
   clib_spinlock_init (&listener->rx_lock);
+  if (!um->csum_offload)
+    listener->cfg_flags |= UDP_CFG_F_NO_CSUM_OFFLOAD;
 
   udp_connection_register_port (lcl_port_ho, lcl->is_ip4);
   return listener->c_c_index;
@@ -225,7 +227,8 @@
 always_inline u32
 udp_push_one_header (vlib_main_t *vm, udp_connection_t *uc, vlib_buffer_t *b)
 {
-  vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port, 1);
+  vlib_buffer_push_udp (b, uc->c_lcl_port, uc->c_rmt_port,
+			udp_csum_offload (uc));
   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
   /* reuse tcp medatada for now */
   vnet_buffer (b)->tcp.connection_index = uc->c_c_index;
@@ -405,6 +408,8 @@
       clib_spinlock_init (&uc->rx_lock);
       uc->c_flags |= TRANSPORT_CONNECTION_F_CLESS;
     }
+  if (!um->csum_offload)
+    uc->cfg_flags |= UDP_CFG_F_NO_CSUM_OFFLOAD;
   uc->next_node_index = rmt->next_node_index;
   uc->next_node_opaque = rmt->next_node_opaque;
 
@@ -519,6 +524,7 @@
     vlib_node_add_next (vm, udp6_local_node.index, udp6_input_node.index);
 
   um->default_mtu = 1500;
+  um->csum_offload = 1;
   return 0;
 }
 
diff --git a/src/vnet/udp/udp.h b/src/vnet/udp/udp.h
index 96316a4..94362ad 100644
--- a/src/vnet/udp/udp.h
+++ b/src/vnet/udp/udp.h
@@ -55,6 +55,24 @@
 #undef _
 } udp_conn_flags_t;
 
+#define foreach_udp_cfg_flag _ (NO_CSUM_OFFLOAD, "no-csum-offload")
+
+typedef enum udp_cfg_flag_bits_
+{
+#define _(sym, str) UDP_CFG_F_##sym##_BIT,
+  foreach_udp_cfg_flag
+#undef _
+    UDP_CFG_N_FLAG_BITS
+} udp_cfg_flag_bits_e;
+
+typedef enum udp_cfg_flag_
+{
+#define _(sym, str) UDP_CFG_F_##sym = 1 << UDP_CFG_F_##sym##_BIT,
+  foreach_udp_cfg_flag
+#undef _
+    UDP_CFG_N_FLAGS
+} __clib_packed udp_cfg_flags_t;
+
 typedef struct
 {
   /** Required for pool_get_aligned */
@@ -62,12 +80,15 @@
   transport_connection_t connection;	/**< must be first */
   clib_spinlock_t rx_lock;		/**< rx fifo lock */
   u8 flags;				/**< connection flags */
+  udp_cfg_flags_t cfg_flags;		/**< configuration flags */
   u16 mss;				/**< connection mss */
   u32 sw_if_index;			/**< connection sw_if_index */
   u32 next_node_index;	/**< Can be used to control next node in output */
   u32 next_node_opaque; /**< Opaque to pass to next node */
 } udp_connection_t;
 
+#define udp_csum_offload(uc) (!((uc)->cfg_flags & UDP_CFG_F_NO_CSUM_OFFLOAD))
+
 typedef struct
 {
   /* Name (a c string). */
@@ -122,6 +143,7 @@
 
   u16 default_mtu;
   u16 msg_id_base;
+  u8 csum_offload;
 
   u8 icmp_send_unreachable_disabled;
 } udp_main_t;
diff --git a/src/vnet/udp/udp_cli.c b/src/vnet/udp/udp_cli.c
index 9787eed..72dd9d8 100644
--- a/src/vnet/udp/udp_cli.c
+++ b/src/vnet/udp/udp_cli.c
@@ -38,6 +38,33 @@
   return s;
 }
 
+static const char *udp_cfg_flags_str[] = {
+#define _(sym, str) str,
+  foreach_udp_cfg_flag
+#undef _
+};
+
+static u8 *
+format_udp_cfg_flags (u8 *s, va_list *args)
+{
+  udp_connection_t *tc = va_arg (*args, udp_connection_t *);
+  int i, last = -1;
+
+  for (i = 0; i < UDP_CFG_N_FLAG_BITS; i++)
+    if (tc->cfg_flags & (1 << i))
+      last = i;
+  if (last >= 0)
+    s = format (s, " cfg: ");
+  for (i = 0; i < last; i++)
+    {
+      if (tc->cfg_flags & (1 << i))
+	s = format (s, "%s, ", udp_cfg_flags_str[i]);
+    }
+  if (last >= 0)
+    s = format (s, "%s", udp_cfg_flags_str[last]);
+  return s;
+}
+
 static const char *udp_connection_flags_str[] = {
 #define _(sym, str) str,
   foreach_udp_connection_flag
@@ -68,8 +95,8 @@
 {
   udp_connection_t *uc = va_arg (*args, udp_connection_t *);
 
-  s = format (s, " index %u flags: %U", uc->c_c_index,
-	      format_udp_connection_flags, uc);
+  s = format (s, " index %u%U flags: %U", uc->c_c_index, format_udp_cfg_flags,
+	      uc, format_udp_connection_flags, uc);
   if (!(uc->flags & UDP_CONN_F_LISTEN))
     s = format (s, " \n sw_if_index: %d, mss: %u\n", uc->sw_if_index, uc->mss);
 
@@ -106,6 +133,8 @@
 	um->default_mtu = tmp;
       else if (unformat (input, "icmp-unreachable-disabled"))
 	um->icmp_send_unreachable_disabled = 1;
+      else if (unformat (input, "no-csum-offload"))
+	um->csum_offload = 0;
       else
 	return clib_error_return (0, "unknown input `%U'",
 				  format_unformat_error, input);
diff --git a/src/vnet/udp/udp_input.c b/src/vnet/udp/udp_input.c
index c11c0d5..1c1d9e8 100644
--- a/src/vnet/udp/udp_input.c
+++ b/src/vnet/udp/udp_input.c
@@ -115,6 +115,7 @@
   uc->c_fib_index = listener->c_fib_index;
   uc->mss = listener->mss;
   uc->flags |= UDP_CONN_F_CONNECTED;
+  uc->cfg_flags = listener->cfg_flags;
 
   if (session_dgram_accept (&uc->connection, listener->c_s_index,
 			    listener->c_thread_index))
diff --git a/src/vnet/udp/udp_output.c b/src/vnet/udp/udp_output.c
index 1252bfb..3ab21d3 100644
--- a/src/vnet/udp/udp_output.c
+++ b/src/vnet/udp/udp_output.c
@@ -81,7 +81,7 @@
 {
   if (uc->c_is_ip4)
     vlib_buffer_push_ip4_custom (vm, b, &uc->c_lcl_ip4, &uc->c_rmt_ip4,
-				 IP_PROTOCOL_UDP, 1 /* csum offload */,
+				 IP_PROTOCOL_UDP, udp_csum_offload (uc),
 				 0 /* is_df */, uc->c_dscp);
   else
     vlib_buffer_push_ip6 (vm, b, &uc->c_lcl_ip6, &uc->c_rmt_ip6,