ipsec: enable ipv6 udp checksum offload

RFC6935 section 5 states that, by default, the UDP checksum must be
computed when originating an IPv6 UDP packet. The default behavior
may be overridden when conditions defined by RFC6936 are satisfied.
However this implementation does not satisfy all the conditions so
the checksum must be computed.

After ESP encryption the packet is an IPv6 UDP packet so set the
l3_hdr_offset and l4_hdr_offset values, and set the UDP_CKSUM
offload flag in the buffer.

Type: improvement

Co-authored-by: Cian Ferriter <cian.ferriter@intel.com>
Change-Id: I9f8c434c9fe9dbddd8890d5ae366984bfcf34067
Signed-off-by: Jeff Shaw <jeffrey.b.shaw@intel.com>
diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c
index 544ff7b..f6d1eca 100644
--- a/src/vnet/ipsec/esp_encrypt.c
+++ b/src/vnet/ipsec/esp_encrypt.c
@@ -589,6 +589,22 @@
 				  async_next, iv, tag, aad, flag);
 }
 
+/* Per RFC6935 section 5, the UDP checksum must be computed when originating
+ * an IPv6 UDP packet. The default behavior may be overridden when conditions
+ * defined by RFC6936 are satisfied. This implementation does not satisfy all
+ * the conditions so the checksum must be computed.
+ */
+static_always_inline void
+set_ip6_udp_cksum_offload (vlib_buffer_t *b, i16 l3_hdr_offset,
+			   i16 l4_hdr_offset)
+{
+  vnet_buffer (b)->l3_hdr_offset = l3_hdr_offset;
+  vnet_buffer (b)->l4_hdr_offset = l4_hdr_offset;
+  vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
+  b->flags |= (VNET_BUFFER_F_IS_IP6 | VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
+	       VNET_BUFFER_F_L4_HDR_OFFSET_VALID);
+}
+
 always_inline uword
 esp_encrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
 		    vlib_frame_t *frame, vnet_link_t lt, int is_tun,
@@ -869,6 +885,15 @@
 	      esp_update_ip4_hdr (ip4, len, /* is_transport */ 0, 0);
 	    }
 
+	  if (ipsec_sa_is_set_UDP_ENCAP (sa0) &&
+	      ipsec_sa_is_set_IS_TUNNEL_V6 (sa0))
+	    {
+	      i16 l3_off = b[0]->current_data - hdr_len;
+	      i16 l4_off = l3_off + sizeof (ip6_header_t);
+
+	      set_ip6_udp_cksum_offload (b[0], l3_off, l4_off);
+	    }
+
 	  dpo = &sa0->dpo;
 	  if (!is_tun)
 	    {
@@ -988,6 +1013,14 @@
 	      esp_fill_udp_hdr (sa0, udp, udp_len);
 	    }
 
+	  if (udp && (VNET_LINK_IP6 == lt))
+	    {
+	      i16 l3_off = b[0]->current_data - hdr_len + l2_len;
+	      i16 l4_off = l3_off + ip_len;
+
+	      set_ip6_udp_cksum_offload (b[0], l3_off, l4_off);
+	    }
+
 	  sync_next[0] = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
 	}