vxlan: Fixed checksum caclculation offset 95/27195/12
authorVladimir Isaev <visaev@netgate.com>
Thu, 21 May 2020 13:34:17 +0000 (16:34 +0300)
committerJohn Lo <loj@cisco.com>
Mon, 8 Jun 2020 14:17:59 +0000 (14:17 +0000)
VXLAN uses csum_offload for IPv6 packets.

But without gso node we have csum calculated only for inner
packet.
This patch adds support for outer header csum calculation.
Checksum for inner packet should be calculated before
interface-output node (for example in vxlan node).

Type: fix

Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
Signed-off-by: Vladimir Isaev <visaev@netgate.com>
Change-Id: Ica68429ede4426293769207cd83c791ebe72fe56

src/vnet/gso/hdr_offset_parser.h
src/vnet/interface_output.c
src/vnet/interface_output.h
src/vnet/ip/ip4_forward.c
src/vnet/ip/ip6_forward.c
src/vnet/pg/input.c
test/test_gso.py
test/test_vxlan.py
test/test_vxlan6.py
test/test_vxlan_gbp.py
test/test_vxlan_gpe.py

index b194333..8fa5cad 100644 (file)
@@ -376,7 +376,7 @@ vnet_generic_outer_header_parser_inline (vlib_buffer_t * b0,
   u16 ethertype = 0;
   u16 l2hdr_sz = 0;
 
-  ASSERT (is_ip4 ^ is_ip6);
+  ASSERT (!(is_ip4 && is_ip6));
 
   if (is_l2)
     {
index 8e3d53a..f9490f3 100644 (file)
@@ -344,22 +344,22 @@ vnet_interface_output_node_inline (vlib_main_t * vm,
                    vnet_calc_checksums_inline
                      (vm, b[0],
                       b[0]->flags & VNET_BUFFER_F_IS_IP4,
-                      b[0]->flags & VNET_BUFFER_F_IS_IP6, 1 /* with gso */ );
+                      b[0]->flags & VNET_BUFFER_F_IS_IP6);
                  if (b[1]->flags & vnet_buffer_offload_flags)
                    vnet_calc_checksums_inline
                      (vm, b[1],
                       b[1]->flags & VNET_BUFFER_F_IS_IP4,
-                      b[1]->flags & VNET_BUFFER_F_IS_IP6, 1 /* with gso */ );
+                      b[1]->flags & VNET_BUFFER_F_IS_IP6);
                  if (b[2]->flags & vnet_buffer_offload_flags)
                    vnet_calc_checksums_inline
                      (vm, b[2],
                       b[2]->flags & VNET_BUFFER_F_IS_IP4,
-                      b[2]->flags & VNET_BUFFER_F_IS_IP6, 1 /* with gso */ );
+                      b[2]->flags & VNET_BUFFER_F_IS_IP6);
                  if (b[3]->flags & vnet_buffer_offload_flags)
                    vnet_calc_checksums_inline
                      (vm, b[3],
                       b[3]->flags & VNET_BUFFER_F_IS_IP4,
-                      b[3]->flags & VNET_BUFFER_F_IS_IP6, 1 /* with gso */ );
+                      b[3]->flags & VNET_BUFFER_F_IS_IP6);
                }
            }
          b += 4;
@@ -410,7 +410,7 @@ vnet_interface_output_node_inline (vlib_main_t * vm,
                vnet_calc_checksums_inline
                  (vm, b[0],
                   b[0]->flags & VNET_BUFFER_F_IS_IP4,
-                  b[0]->flags & VNET_BUFFER_F_IS_IP6, 1 /* with gso */ );
+                  b[0]->flags & VNET_BUFFER_F_IS_IP6);
            }
          b += 1;
        }
index c408fca..4b082b3 100644 (file)
@@ -82,59 +82,29 @@ vnet_calc_ip6_checksums (vlib_main_t * vm, vlib_buffer_t * b,
 
 static_always_inline void
 vnet_calc_checksums_inline (vlib_main_t * vm, vlib_buffer_t * b,
-                           int is_ip4, int is_ip6, int with_gso)
+                           int is_ip4, int is_ip6)
 {
   ip4_header_t *ip4;
   ip6_header_t *ip6;
   tcp_header_t *th;
   udp_header_t *uh;
 
-  if (with_gso)
-    {
-      generic_header_offset_t gho = { 0 };
-      vnet_generic_header_offset_parser (b, &gho, 1 /* l2 */ , is_ip4,
-                                        is_ip6);
-
-      ASSERT (gho.gho_flags ^ (GHO_F_IP4 | GHO_F_IP6));
-
-      vnet_get_inner_header (b, &gho);
-
-      ip4 = (ip4_header_t *)
-       (vlib_buffer_get_current (b) + gho.l3_hdr_offset);
-      ip6 = (ip6_header_t *)
-       (vlib_buffer_get_current (b) + gho.l3_hdr_offset);
-      th = (tcp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset);
-      uh = (udp_header_t *) (vlib_buffer_get_current (b) + gho.l4_hdr_offset);
+  ASSERT (!(is_ip4 && is_ip6));
 
-      if (gho.gho_flags & GHO_F_IP4)
-       {
-         vnet_calc_ip4_checksums (vm, b, ip4, th, uh);
-       }
-      else if (gho.gho_flags & GHO_F_IP6)
-       {
-         vnet_calc_ip6_checksums (vm, b, ip6, th, uh);
-       }
+  ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
+  ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
+  th = (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
+  uh = (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
 
-      vnet_get_outer_header (b, &gho);
+  if (is_ip4)
+    {
+      vnet_calc_ip4_checksums (vm, b, ip4, th, uh);
     }
-  else
+  else if (is_ip6)
     {
-      ASSERT (!(is_ip4 && is_ip6));
-
-      ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
-      ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
-      th = (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
-      uh = (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
-
-      if (is_ip4)
-       {
-         vnet_calc_ip4_checksums (vm, b, ip4, th, uh);
-       }
-      if (is_ip6)
-       {
-         vnet_calc_ip6_checksums (vm, b, ip6, th, uh);
-       }
+      vnet_calc_ip6_checksums (vm, b, ip6, th, uh);
     }
+
   b->flags &= ~VNET_BUFFER_F_OFFLOAD_TCP_CKSUM;
   b->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM;
   b->flags &= ~VNET_BUFFER_F_OFFLOAD_IP_CKSUM;
index b76da0a..0fa9da2 100644 (file)
@@ -2241,8 +2241,7 @@ ip4_rewrite_inline_with_gso (vlib_main_t * vm,
          next[0] = next_index;
          if (is_midchain)
            vnet_calc_checksums_inline (vm, b[0], 1 /* is_ip4 */ ,
-                                       0 /* is_ip6 */ ,
-                                       0 /* with gso */ );
+                                       0 /* is_ip6 */ );
        }
       else
        {
@@ -2267,8 +2266,7 @@ ip4_rewrite_inline_with_gso (vlib_main_t * vm,
          next[1] = next_index;
          if (is_midchain)
            vnet_calc_checksums_inline (vm, b[1], 1 /* is_ip4 */ ,
-                                       0 /* is_ip6 */ ,
-                                       0 /* with gso */ );
+                                       0 /* is_ip6 */ );
        }
       else
        {
@@ -2419,8 +2417,7 @@ ip4_rewrite_inline_with_gso (vlib_main_t * vm,
          if (is_midchain)
            {
              vnet_calc_checksums_inline (vm, b[0], 1 /* is_ip4 */ ,
-                                         0 /* is_ip6 */ ,
-                                         0 /* with gso */ );
+                                         0 /* is_ip6 */ );
 
              /* Guess we are only writing on ipv4 header. */
              vnet_rewrite_one_header (adj0[0], ip0, sizeof (ip4_header_t));
@@ -2527,8 +2524,7 @@ ip4_rewrite_inline_with_gso (vlib_main_t * vm,
            {
              /* this acts on the packet that is about to be encapped */
              vnet_calc_checksums_inline (vm, b[0], 1 /* is_ip4 */ ,
-                                         0 /* is_ip6 */ ,
-                                         0 /* with gso */ );
+                                         0 /* is_ip6 */ );
 
              /* Guess we are only writing on ipv4 header. */
              vnet_rewrite_one_header (adj0[0], ip0, sizeof (ip4_header_t));
index e4d8fc0..d6ef8c8 100644 (file)
@@ -1936,11 +1936,9 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
              /* before we paint on the next header, update the L4
               * checksums if required, since there's no offload on a tunnel */
              vnet_calc_checksums_inline (vm, p0, 0 /* is_ip4 */ ,
-                                         1 /* is_ip6 */ ,
-                                         0 /* with gso */ );
+                                         1 /* is_ip6 */ );
              vnet_calc_checksums_inline (vm, p1, 0 /* is_ip4 */ ,
-                                         1 /* is_ip6 */ ,
-                                         0 /* with gso */ );
+                                         1 /* is_ip6 */ );
 
              /* Guess we are only writing on ipv6 header. */
              vnet_rewrite_two_headers (adj0[0], adj1[0],
@@ -2036,8 +2034,7 @@ ip6_rewrite_inline_with_gso (vlib_main_t * vm,
          if (is_midchain)
            {
              vnet_calc_checksums_inline (vm, p0, 0 /* is_ip4 */ ,
-                                         1 /* is_ip6 */ ,
-                                         0 /* with gso */ );
+                                         1 /* is_ip6 */ );
 
              /* Guess we are only writing on ip6 header. */
              vnet_rewrite_one_header (adj0[0], ip0, sizeof (ip6_header_t));
index 8ba3b36..483add0 100644 (file)
@@ -1530,10 +1530,9 @@ pg_input_trace (pg_main_t * pg,
 }
 
 static_always_inline void
-fill_gso_buffer_flags (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
-                      u32 packet_data_size)
+fill_buffer_offload_flags (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
+                          int gso_enabled, u32 gso_size)
 {
-
   for (int i = 0; i < n_buffers; i++)
     {
       vlib_buffer_t *b0 = vlib_get_buffer (vm, buffers[i]);
@@ -1586,16 +1585,21 @@ fill_gso_buffer_flags (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
             VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
             VNET_BUFFER_F_L4_HDR_OFFSET_VALID);
        }
+
       if (l4_proto == IP_PROTOCOL_TCP)
        {
-         b0->flags |= (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM | VNET_BUFFER_F_GSO);
+         b0->flags |= VNET_BUFFER_F_OFFLOAD_TCP_CKSUM;
          tcp_header_t *tcp = (tcp_header_t *) (vlib_buffer_get_current (b0) +
                                                vnet_buffer
                                                (b0)->l4_hdr_offset);
-         l4_hdr_sz = tcp_header_bytes (tcp);
          tcp->checksum = 0;
-         vnet_buffer2 (b0)->gso_l4_hdr_sz = l4_hdr_sz;
-         vnet_buffer2 (b0)->gso_size = packet_data_size;
+         if (gso_enabled)
+           {
+             b0->flags |= VNET_BUFFER_F_GSO;
+             l4_hdr_sz = tcp_header_bytes (tcp);
+             vnet_buffer2 (b0)->gso_l4_hdr_sz = l4_hdr_sz;
+             vnet_buffer2 (b0)->gso_size = gso_size;
+           }
        }
       else if (l4_proto == IP_PROTOCOL_UDP)
        {
@@ -1603,7 +1607,6 @@ fill_gso_buffer_flags (vlib_main_t * vm, u32 * buffers, u32 n_buffers,
          udp_header_t *udp = (udp_header_t *) (vlib_buffer_get_current (b0) +
                                                vnet_buffer
                                                (b0)->l4_hdr_offset);
-         vnet_buffer2 (b0)->gso_l4_hdr_sz = sizeof (*udp);
          udp->checksum = 0;
        }
     }
@@ -1700,8 +1703,14 @@ pg_generate_packets (vlib_node_runtime_t * node,
            vnet_buffer (b)->feature_arc_index = feature_arc_index;
          }
 
-      if (pi->gso_enabled)
-       fill_gso_buffer_flags (vm, to_next, n_this_frame, pi->gso_size);
+      if (pi->gso_enabled ||
+         (s->buffer_flags & (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM |
+                             VNET_BUFFER_F_OFFLOAD_UDP_CKSUM |
+                             VNET_BUFFER_F_OFFLOAD_IP_CKSUM)))
+       {
+         fill_buffer_offload_flags (vm, to_next, n_this_frame,
+                                    pi->gso_enabled, pi->gso_size);
+       }
 
       n_trace = vlib_get_trace_count (vm, node);
       if (n_trace > 0)
index 29a54eb..7189104 100644 (file)
@@ -444,7 +444,7 @@ class TestGSO(VppTestCase):
             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
             self.assertEqual(rx[IPv6].src, self.pg0.local_ip6)
             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
-            self.assert_udp_checksum_valid(rx)
+            self.assert_udp_checksum_valid(rx, ignore_zero_checksum=False)
             self.assertEqual(rx[VXLAN].vni, 10)
             inner = rx[VXLAN].payload
             self.assertEqual(rx[IPv6].plen - 8 - 8, len(inner))
@@ -474,7 +474,7 @@ class TestGSO(VppTestCase):
             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
             self.assertEqual(rx[IPv6].src, self.pg0.local_ip6)
             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
-            self.assert_udp_checksum_valid(rx)
+            self.assert_udp_checksum_valid(rx, ignore_zero_checksum=False)
             self.assertEqual(rx[VXLAN].vni, 10)
             inner = rx[VXLAN].payload
             self.assertEqual(rx[IPv6].plen - 8 - 8, len(inner))
index 1793e49..54d0c2d 100644 (file)
@@ -80,7 +80,8 @@ class TestVxlan(BridgeDomain, VppTestCase):
         # Verify UDP destination port is VXLAN 4789, source UDP port could be
         #  arbitrary.
         self.assertEqual(pkt[UDP].dport, type(self).dport)
-        # TODO: checksum check
+        # Verify UDP checksum
+        self.assert_udp_checksum_valid(pkt)
         # Verify VNI
         self.assertEqual(pkt[VXLAN].vni, vni)
 
index 3a11ea9..d9e2d81 100644 (file)
@@ -6,7 +6,8 @@ from framework import VppTestCase, VppTestRunner
 from template_bd import BridgeDomain
 
 from scapy.layers.l2 import Ether
-from scapy.layers.inet6 import IPv6, UDP
+from scapy.packet import Raw
+from scapy.layers.inet6 import IP, IPv6, UDP
 from scapy.layers.vxlan import VXLAN
 
 import util
@@ -80,7 +81,8 @@ class TestVxlan6(BridgeDomain, VppTestCase):
         # Verify UDP destination port is VXLAN 4789, source UDP port could be
         #  arbitrary.
         self.assertEqual(pkt[UDP].dport, type(self).dport)
-        # TODO: checksum check
+        # Verify UDP checksum
+        self.assert_udp_checksum_valid(pkt, ignore_zero_checksum=False)
         # Verify VNI
         self.assertEqual(pkt[VXLAN].vni, vni)
 
@@ -190,6 +192,35 @@ class TestVxlan6(BridgeDomain, VppTestCase):
         self.logger.info(self.vapi.cli("show bridge-domain 3 detail"))
         self.logger.info(self.vapi.cli("show vxlan tunnel"))
 
+    def test_encap_fragmented_packet(self):
+        """ Encapsulation test send fragments from pg1
+        Verify receipt of encapsulated frames on pg0
+        """
+
+        frame = (Ether(src='00:00:00:00:00:02', dst='00:00:00:00:00:01') /
+                 IP(src='4.3.2.1', dst='1.2.3.4') /
+                 UDP(sport=20000, dport=10000) /
+                 Raw(b'\xa5' * 1000))
+
+        frags = util.fragment_rfc791(frame, 400)
+
+        self.pg1.add_stream(frags)
+
+        self.pg0.enable_capture()
+
+        self.pg_start()
+
+        out = self.pg0.get_capture(3)
+
+        payload = []
+        for pkt in out:
+            payload.append(self.decapsulate(pkt))
+            self.check_encapsulation(pkt, self.single_tunnel_vni)
+
+        reassembled = util.reassemble4(payload)
+
+        self.assertEqual(Ether(raw(frame))[IP], reassembled[IP])
+
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index 79eb23d..f332ace 100644 (file)
@@ -80,7 +80,8 @@ class TestVxlanGbp(VppTestCase):
         # Verify UDP destination port is VXLAN GBP 48879, source UDP port could
         # be arbitrary.
         self.assertEqual(pkt[UDP].dport, type(self).dport)
-        # TODO: checksum check
+        # Verify UDP checksum
+        self.assert_udp_checksum_valid(pkt)
         # Verify VNI
         # pkt.show()
         self.assertEqual(pkt[VXLAN].vni, vni)
index 5c3ac16..c5d6bf0 100644 (file)
@@ -80,6 +80,8 @@ class TestVxlanGpe(BridgeDomain, VppTestCase):
         # Verify UDP destination port is VXLAN-GPE 4790, source UDP port
         #  could be arbitrary.
         self.assertEqual(pkt[UDP].dport, type(self).dport)
+        # Verify UDP checksum
+        self.assert_udp_checksum_valid(pkt)
         # Verify VNI
         self.assertEqual(pkt[VXLAN].vni, vni)