ip: Fragmentation fixes
[vpp.git] / src / vnet / ip / ip_frag.c
index 628d9d6..54efb63 100644 (file)
 
 #include <vnet/ip/ip.h>
 
+/*
+ * Copy the mpls header if present.
+ * The current is pointing to the ip header.
+ * Adjust the buffer and point to the mpls headers on these fragments
+ * before sending the packet back to mpls-output node.
+ */
+static inline void
+copy_mpls_hdr (vlib_buffer_t * to_b, vlib_buffer_t * from_b)
+{
+  if ((vnet_buffer (from_b)->ip_frag.flags) & IP_FRAG_FLAG_MPLS_HEADER)
+    {
+      u8 mpls_hdr_length = vnet_buffer (from_b)->mpls.mpls_hdr_length;
+      u8 *org_from_mpls_packet =
+       from_b->data + (from_b->current_data - mpls_hdr_length);
+      clib_memcpy_fast ((to_b->data - mpls_hdr_length), org_from_mpls_packet,
+                       mpls_hdr_length);
+      vlib_buffer_advance (to_b, -vnet_buffer (to_b)->mpls.mpls_hdr_length);
+    }
+}
 
 typedef struct
 {
@@ -38,8 +57,8 @@ format_ip_frag_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   ip_frag_trace_t *t = va_arg (*args, ip_frag_trace_t *);
-  s = format (s, "IPv%s mtu: %u fragments: %u",
-             t->ipv6 ? "6" : "4", t->mtu, t->n_fragments);
+  s = format (s, "IPv%s mtu: %u fragments: %u next: %d",
+             t->ipv6 ? "6" : "4", t->mtu, t->n_fragments, t->next);
   return s;
 }
 
@@ -61,6 +80,21 @@ frag_set_sw_if_index (vlib_buffer_t * to, vlib_buffer_t * from)
     vnet_buffer (from)->ip.adj_index[VLIB_RX];
   vnet_buffer (to)->ip.adj_index[VLIB_TX] =
     vnet_buffer (from)->ip.adj_index[VLIB_TX];
+
+  /* Copy QoS Bits */
+  if (PREDICT_TRUE (from->flags & VNET_BUFFER_F_QOS_DATA_VALID))
+    {
+      vnet_buffer2 (to)->qos = vnet_buffer2 (from)->qos;
+      to->flags |= VNET_BUFFER_F_QOS_DATA_VALID;
+    }
+
+  /* Copy mpls opaque data */
+  if ((vnet_buffer (from)->ip_frag.flags) & IP_FRAG_FLAG_MPLS_HEADER)
+    {
+      vnet_buffer (to)->mpls.pyld_proto = vnet_buffer (from)->mpls.pyld_proto;
+      vnet_buffer (to)->mpls.mpls_hdr_length =
+       vnet_buffer (from)->mpls.mpls_hdr_length;
+    }
 }
 
 static vlib_buffer_t *
@@ -71,9 +105,6 @@ frag_buffer_alloc (vlib_buffer_t * org_b, u32 * bi)
     return 0;
 
   vlib_buffer_t *b = vlib_get_buffer (vm, *bi);
-  vlib_buffer_free_list_t *fl =
-    vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
-  vlib_buffer_init_for_free_list (b, fl);
   VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b);
   vlib_buffer_copy_trace_flag (vm, org_b, *bi);
 
@@ -101,7 +132,9 @@ ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
   ip4 = (ip4_header_t *) vlib_buffer_get_current (from_b);
 
   rem = clib_net_to_host_u16 (ip4->length) - sizeof (ip4_header_t);
-  max = (mtu - sizeof (ip4_header_t)) & ~0x7;
+  max =
+    (clib_min (mtu, vlib_buffer_get_default_data_size (vm)) -
+     sizeof (ip4_header_t)) & ~0x7;
 
   if (rem >
       (vlib_buffer_length_in_chain (vm, from_b) - sizeof (ip4_header_t)))
@@ -152,7 +185,7 @@ ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
       ip4_header_t *to_ip4;
       u8 *to_data;
 
-      len = (rem > (mtu - sizeof (ip4_header_t)) ? max : rem);
+      len = (rem > max ? max : rem);
       if (len != rem)          /* Last fragment does not need to divisible by 8 */
        len &= ~0x7;
       if ((to_b = frag_buffer_alloc (org_from_b, &to_bi)) == 0)
@@ -164,9 +197,20 @@ ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
       frag_set_sw_if_index (to_b, org_from_b);
 
       /* Copy ip4 header */
-      clib_memcpy (to_b->data, org_from_packet, sizeof (ip4_header_t));
+      clib_memcpy_fast (to_b->data, org_from_packet, sizeof (ip4_header_t));
       to_ip4 = vlib_buffer_get_current (to_b);
       to_data = (void *) (to_ip4 + 1);
+      vnet_buffer (to_b)->l3_hdr_offset = to_b->current_data;
+      to_b->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
+
+      if (from_b->flags & VNET_BUFFER_F_L4_HDR_OFFSET_VALID)
+       {
+         vnet_buffer (to_b)->l4_hdr_offset =
+           (vnet_buffer (to_b)->l3_hdr_offset +
+            (vnet_buffer (from_b)->l4_hdr_offset -
+             vnet_buffer (from_b)->l3_hdr_offset));
+         to_b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
+       }
 
       /* Spin through from buffers filling up the to buffer */
       u16 left_in_to_buffer = len, to_ptr = 0;
@@ -177,7 +221,7 @@ ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
          /* Figure out how many bytes we can safely copy */
          bytes_to_copy = left_in_to_buffer <= left_in_from_buffer ?
            left_in_to_buffer : left_in_from_buffer;
-         clib_memcpy (to_data + to_ptr, from_data + ptr, bytes_to_copy);
+         clib_memcpy_fast (to_data + to_ptr, from_data + ptr, bytes_to_copy);
          left_in_to_buffer -= bytes_to_copy;
          ptr += bytes_to_copy;
          left_in_from_buffer -= bytes_to_copy;
@@ -199,6 +243,7 @@ ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
        }
 
       to_b->current_length = len + sizeof (ip4_header_t);
+      to_b->flags |= VNET_BUFFER_F_IS_IP4;
 
       to_ip4->fragment_id = ip_frag_id;
       to_ip4->flags_and_fragment_offset =
@@ -208,6 +253,9 @@ ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
       to_ip4->length = clib_host_to_net_u16 (len + sizeof (ip4_header_t));
       to_ip4->checksum = ip4_header_checksum (to_ip4);
 
+      /* we've just done the IP checksum .. */
+      to_b->flags &= ~VNET_BUFFER_F_OFFLOAD_IP_CKSUM;
+
       if (vnet_buffer (org_from_b)->ip_frag.flags & IP_FRAG_FLAG_IP4_HEADER)
        {
          /* Encapsulating ipv4 header */
@@ -226,6 +274,10 @@ ip4_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
            clib_host_to_net_u16 (to_b->current_length -
                                  sizeof (*encap_header6));
        }
+
+      /* Copy mpls header if present */
+      copy_mpls_hdr (to_b, org_from_b);
+
       rem -= len;
       fo += len;
     }
@@ -440,11 +492,24 @@ ip6_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
       frag_set_sw_if_index (to_b, org_from_b);
 
       /* Copy ip6 header */
-      clib_memcpy (to_b->data, ip6, sizeof (ip6_header_t));
+      clib_memcpy_fast (to_b->data, ip6, sizeof (ip6_header_t));
       to_ip6 = vlib_buffer_get_current (to_b);
       to_frag_hdr = (ip6_frag_hdr_t *) (to_ip6 + 1);
       to_data = (void *) (to_frag_hdr + 1);
 
+      vnet_buffer (to_b)->l3_hdr_offset = to_b->current_data;
+      to_b->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
+
+      if (from_b->flags & VNET_BUFFER_F_L4_HDR_OFFSET_VALID)
+       {
+         vnet_buffer (to_b)->l4_hdr_offset =
+           (vnet_buffer (to_b)->l3_hdr_offset +
+            (vnet_buffer (from_b)->l4_hdr_offset -
+             vnet_buffer (from_b)->l3_hdr_offset));
+         to_b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
+       }
+      to_b->flags |= VNET_BUFFER_F_IS_IP6;
+
       /* Spin through from buffers filling up the to buffer */
       u16 left_in_to_buffer = len, to_ptr = 0;
       while (1)
@@ -454,7 +519,7 @@ ip6_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
          /* Figure out how many bytes we can safely copy */
          bytes_to_copy = left_in_to_buffer <= left_in_from_buffer ?
            left_in_to_buffer : left_in_from_buffer;
-         clib_memcpy (to_data + to_ptr, from_data + ptr, bytes_to_copy);
+         clib_memcpy_fast (to_data + to_ptr, from_data + ptr, bytes_to_copy);
          left_in_to_buffer -= bytes_to_copy;
          ptr += bytes_to_copy;
          left_in_from_buffer -= bytes_to_copy;
@@ -486,6 +551,9 @@ ip6_frag_do_fragment (vlib_main_t * vm, u32 from_bi, u32 ** buffer,
       to_frag_hdr->next_hdr = ip6->protocol;
       to_frag_hdr->rsv = 0;
 
+      /* Copy mpls header if present */
+      copy_mpls_hdr (to_b, org_from_b);
+
       rem -= len;
       fo += len;
     }
@@ -511,8 +579,10 @@ VLIB_REGISTER_NODE (ip4_frag_node) = {
   .n_next_nodes = IP4_FRAG_N_NEXT,
   .next_nodes = {
     [IP4_FRAG_NEXT_IP4_REWRITE] = "ip4-rewrite",
+    [IP4_FRAG_NEXT_IP4_REWRITE_MIDCHAIN] = "ip4-midchain",
     [IP4_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
     [IP4_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
+    [IP4_FRAG_NEXT_MPLS_OUTPUT] = "mpls-output",
     [IP4_FRAG_NEXT_ICMP_ERROR] = "ip4-icmp-error",
     [IP4_FRAG_NEXT_DROP] = "ip4-drop"
   },
@@ -533,8 +603,10 @@ VLIB_REGISTER_NODE (ip6_frag_node) = {
   .n_next_nodes = IP6_FRAG_N_NEXT,
   .next_nodes = {
     [IP6_FRAG_NEXT_IP6_REWRITE] = "ip6-rewrite",
+    [IP6_FRAG_NEXT_IP6_REWRITE_MIDCHAIN] = "ip6-midchain",
     [IP6_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
     [IP6_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
+    [IP6_FRAG_NEXT_MPLS_OUTPUT] = "mpls-output",
     [IP6_FRAG_NEXT_DROP] = "ip6-drop"
   },
 };