ip6: fix l4 checksum with hop-by-hop header 35/24835/2
authorMatthew Smith <mgsmith@netgate.com>
Wed, 5 Feb 2020 17:46:40 +0000 (11:46 -0600)
committerNeale Ranns <nranns@cisco.com>
Mon, 10 Feb 2020 07:54:19 +0000 (07:54 +0000)
L4 checksums for IPv6 should be calculated using a pseudo header that
includes the source/destination addresses, payload length, and payload
protocol.

ip6_tcp_udp_icmp_compute_checksum() was using the payload length and
protocol from the IPv6 header. If there is a hop-by-hop header (or any
other extension header), the payload length used for the pseudo header
should only include the upper layer header and payload and not the
extension header bytes. Same deal with the protocol, the upper layer
next header value should be used instead of the extension header.

Type: fix
Fixes: cb9cadad57

Change-Id: Ifa2c9ad41c0fc4eea674f0671255b637c8e01f71
Signed-off-by: Matthew Smith <mgsmith@netgate.com>
src/vnet/ip/ip6_forward.c

index 69ae86d..f7f7b78 100644 (file)
@@ -1021,31 +1021,23 @@ u16
 ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
                                   ip6_header_t * ip0, int *bogus_lengthp)
 {
-  ip_csum_t sum0;
-  u16 payload_length_host_byte_order;
+  ip_csum_t sum0 = 0;
+  u16 payload_length, payload_length_host_byte_order;
   u32 i;
   u32 headers_size = sizeof (ip0[0]);
   u8 *data_this_buffer;
+  u8 next_hdr = ip0->protocol;
 
   ASSERT (bogus_lengthp);
   *bogus_lengthp = 0;
 
-  /* Initialize checksum with ip header. */
-  sum0 = ip0->payload_length + clib_host_to_net_u16 (ip0->protocol);
   payload_length_host_byte_order = clib_net_to_host_u16 (ip0->payload_length);
   data_this_buffer = (u8 *) (ip0 + 1);
-
-  for (i = 0; i < ARRAY_LEN (ip0->src_address.as_uword); i++)
-    {
-      sum0 = ip_csum_with_carry
-       (sum0, clib_mem_unaligned (&ip0->src_address.as_uword[i], uword));
-      sum0 = ip_csum_with_carry
-       (sum0, clib_mem_unaligned (&ip0->dst_address.as_uword[i], uword));
-    }
+  payload_length = ip0->payload_length;
 
   /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets)
    * or UDP-Ping packets */
-  if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
+  if (PREDICT_FALSE (next_hdr == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
     {
       u32 skip_bytes;
       ip6_hop_by_hop_ext_t *ext_hdr =
@@ -1060,6 +1052,24 @@ ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
 
       payload_length_host_byte_order -= skip_bytes;
       headers_size += skip_bytes;
+
+      /* pseudo-header adjustments:
+       *   exclude ext header bytes from payload length
+       *   use payload IP proto rather than ext header IP proto
+       */
+      payload_length = clib_host_to_net_u16 (payload_length_host_byte_order);
+      next_hdr = ext_hdr->next_hdr;
+    }
+
+  /* Initialize checksum with ip pseudo-header. */
+  sum0 = payload_length + clib_host_to_net_u16 (next_hdr);
+
+  for (i = 0; i < ARRAY_LEN (ip0->src_address.as_uword); i++)
+    {
+      sum0 = ip_csum_with_carry
+       (sum0, clib_mem_unaligned (&ip0->src_address.as_uword[i], uword));
+      sum0 = ip_csum_with_carry
+       (sum0, clib_mem_unaligned (&ip0->dst_address.as_uword[i], uword));
     }
 
   if (p0)