dhcp4:(VPP-1483) linearize chained packets before handling 59/15759/6
authorEyal Bari <ebari@cisco.com>
Mon, 5 Nov 2018 11:29:25 +0000 (13:29 +0200)
committerJohn Lo <loj@cisco.com>
Tue, 20 Nov 2018 02:52:39 +0000 (02:52 +0000)
dhcp packets might (when flooded) arrive in chains of cloned buffers

Change-Id: Ifddecd656b6a5d6ba8cd94184f5c021684e35548
Signed-off-by: Eyal Bari <ebari@cisco.com>
src/vlib/buffer.h
src/vlib/buffer_funcs.h
src/vnet/dhcp/dhcp4_packet.h
src/vnet/dhcp/dhcp4_proxy_node.c

index 493d111..02b1709 100644 (file)
@@ -302,7 +302,7 @@ vlib_buffer_get_tail (vlib_buffer_t * b)
  * @return      pointer to beginning of uninitialized data
  */
 always_inline void *
-vlib_buffer_put_uninit (vlib_buffer_t * b, u8 size)
+vlib_buffer_put_uninit (vlib_buffer_t * b, u16 size)
 {
   void *p = vlib_buffer_get_tail (b);
   /* XXX make sure there's enough space */
index d15ef57..e8ccc86 100644 (file)
@@ -1267,6 +1267,70 @@ vlib_buffer_chain_compress (vlib_main_t * vm,
         (first->flags & VLIB_BUFFER_NEXT_PRESENT));
 }
 
+/**
+ * @brief linearize buffer chain - the first buffer is filled, if needed,
+ * buffers are allocated and filled, returns free space in last buffer or
+ * negative on failure
+ *
+ * @param[in] vm - vlib_main
+ * @param[in,out] first - first buffer in chain
+ */
+always_inline int
+vlib_buffer_chain_linearize (vlib_main_t * vm, vlib_buffer_t * first)
+{
+  vlib_buffer_t *b = first;
+  vlib_buffer_free_list_t *fl =
+    vlib_buffer_get_free_list (vm, vlib_buffer_get_free_list_index (b));
+  u32 buf_len = fl->n_data_bytes;
+  // free buffer chain starting from the second buffer
+  int free_count = (b->flags & VLIB_BUFFER_NEXT_PRESENT) != 0;
+  u32 chain_to_free = b->next_buffer;
+
+  u32 len = vlib_buffer_length_in_chain (vm, b);
+  u32 free_len = buf_len - b->current_data - b->current_length;
+  int alloc_len = clib_max (len - free_len, 0);        //use the free len in the first buffer
+  int n_buffers = (alloc_len + buf_len - 1) / buf_len;
+  u32 new_buffers[n_buffers];
+
+  u32 n_alloc = vlib_buffer_alloc (vm, new_buffers, n_buffers);
+  if (n_alloc != n_buffers)
+    {
+      vlib_buffer_free_no_next (vm, new_buffers, n_alloc);
+      return -1;
+    }
+
+  vlib_buffer_t *s = b;
+  while (s->flags & VLIB_BUFFER_NEXT_PRESENT)
+    {
+      s = vlib_get_buffer (vm, s->next_buffer);
+      int d_free_len = buf_len - b->current_data - b->current_length;
+      ASSERT (d_free_len >= 0);
+      // chain buf and split write
+      u32 copy_len = clib_min (d_free_len, s->current_length);
+      u8 *d = vlib_buffer_put_uninit (b, copy_len);
+      clib_memcpy (d, vlib_buffer_get_current (s), copy_len);
+      int rest = s->current_length - copy_len;
+      if (rest > 0)
+       {
+         //prev buf is full
+         ASSERT (vlib_buffer_get_tail (b) == b->data + buf_len);
+         ASSERT (n_buffers > 0);
+         b = vlib_buffer_chain_buffer (vm, b, new_buffers[--n_buffers]);
+         //make full use of the new buffers
+         b->current_data = 0;
+         d = vlib_buffer_put_uninit (b, rest);
+         clib_memcpy (d, vlib_buffer_get_current (s) + copy_len, rest);
+       }
+    }
+  vlib_buffer_free (vm, &chain_to_free, free_count);
+  b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
+  if (b == first)              /* no buffers addeed */
+    b->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
+  ASSERT (len == vlib_buffer_length_in_chain (vm, first));
+  ASSERT (n_buffers == 0);
+  return buf_len - b->current_data - b->current_length;
+}
+
 #endif /* included_vlib_buffer_funcs_h */
 
 /*
index 9fbeb02..3076dd9 100644 (file)
  */
 #include <vnet/ip/ip4_packet.h>
 
+typedef struct
+{
+  u8 option;
+  u8 length;
+  union
+  {
+    u8 data[0];
+    u32 data_as_u32[0];
+  };
+} __attribute__ ((packed)) dhcp_option_t;
+
 typedef struct
 {
   u8 opcode;                   /* 1 = request, 2 = reply */
@@ -37,20 +48,9 @@ typedef struct
   u8 server_name[64];
   u8 boot_filename[128];
   ip4_address_t magic_cookie;
-  u8 options[0];
+  dhcp_option_t options[0];
 } dhcp_header_t;
 
-typedef struct
-{
-  u8 option;
-  u8 length;
-  union
-  {
-    u8 data[0];
-    u32 data_as_u32[0];
-  };
-} __attribute__ ((packed)) dhcp_option_t;
-
 typedef enum
 {
   DHCP_PACKET_DISCOVER = 1,
@@ -63,6 +63,7 @@ typedef enum
 typedef enum dhcp_packet_option_t_
 {
   DHCP_PACKET_OPTION_MSG_TYPE = 53,
+  DHCP_PACKET_OPTION_END = 0xff,
 } dhcp_packet_option_t;
 
 /* charming antique: 99.130.83.99 is the dhcp magic cookie */
index 99e5a73..c1ae514 100644 (file)
@@ -134,15 +134,14 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
          u32 error0 = (u32) ~ 0;
          u32 sw_if_index = 0;
          u32 original_sw_if_index = 0;
-         u8 *end = NULL;
          u32 fib_index;
          dhcp_proxy_t *proxy;
          dhcp_server_t *server;
          u32 rx_sw_if_index;
-         dhcp_option_t *o;
+         dhcp_option_t *o, *end;
          u32 len = 0;
-         vlib_buffer_free_list_t *fl;
          u8 is_discover = 0;
+         int space_left;
 
          bi0 = from[0];
          from += 1;
@@ -171,7 +170,6 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
            }
 
          rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-
          fib_index = im->fib_index_by_sw_if_index[rx_sw_if_index];
          proxy = dhcp_get_proxy (dpm, fib_index, FIB_PROTOCOL_IP4);
 
@@ -183,6 +181,18 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
              goto do_trace;
            }
 
+         space_left = vlib_buffer_chain_linearize (vm, b0);
+         /* cant parse chains...
+          * and we need some space for option 82*/
+         if ((b0->flags & VLIB_BUFFER_NEXT_PRESENT) != 0 ||
+             space_left < VPP_DHCP_OPTION82_SIZE)
+           {
+             error0 = DHCP_PROXY_ERROR_PKT_TOO_BIG;
+             next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
+             pkts_too_big++;
+             goto do_trace;
+           }
+
          server = &proxy->dhcp_servers[0];
          vlib_buffer_advance (b0, -(sizeof (*ip0)));
          ip0 = vlib_buffer_get_current (b0);
@@ -210,17 +220,14 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
          /* Send to DHCP server via the configured FIB */
          vnet_buffer (b0)->sw_if_index[VLIB_TX] = server->server_fib_index;
 
-         h0->gateway_ip_address.as_u32 = proxy->dhcp_src_address.ip4.as_u32;
+         h0->gateway_ip_address = proxy->dhcp_src_address.ip4;
          pkts_to_server++;
 
-         o = (dhcp_option_t *) h0->options;
-
-         fib_index = im->fib_index_by_sw_if_index
-           [vnet_buffer (b0)->sw_if_index[VLIB_RX]];
+         o = h0->options;
+         end = (void *) vlib_buffer_get_tail (b0);
 
-         end = b0->data + b0->current_data + b0->current_length;
          /* TLVs are not performance-friendly... */
-         while (o->option != 0xFF /* end of options */  && (u8 *) o < end)
+         while (o->option != DHCP_PACKET_OPTION_END && o < end)
            {
              if (DHCP_PACKET_OPTION_MSG_TYPE == o->option)
                {
@@ -229,30 +236,16 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
                      is_discover = 1;
                    }
                }
-             o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
+             o = (dhcp_option_t *) (o->data + o->length);
            }
 
-         fl =
-           vlib_buffer_get_free_list (vm,
-                                      vlib_buffer_get_free_list_index (b0));
-         // start write at (option*)o, some packets have padding
-         if (((u8 *) o - (u8 *) b0->data + VPP_DHCP_OPTION82_SIZE) >
-             fl->n_data_bytes)
-           {
-             next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
-             pkts_too_big++;
-             goto do_trace;
-           }
-
-         if ((o->option == 0xFF) && ((u8 *) o <= end))
+         if (o->option == DHCP_PACKET_OPTION_END && o <= end)
            {
              vnet_main_t *vnm = vnet_get_main ();
              u16 old_l0, new_l0;
              ip4_address_t _ia0, *ia0 = &_ia0;
              dhcp_vss_t *vss;
              vnet_sw_interface_t *swif;
-             sw_if_index = 0;
-             original_sw_if_index = 0;
 
              original_sw_if_index = sw_if_index =
                vnet_buffer (b0)->sw_if_index[VLIB_RX];
@@ -286,7 +279,7 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
              o->data[7] = 4;   /* length 4 */
              u32 *o_addr = (u32 *) & o->data[8];
              *o_addr = ia0->as_u32;
-             o->data[12] = 0xFF;
+             o->data[12] = DHCP_PACKET_OPTION_END;
 
              vss = dhcp_get_vss_info (dpm, fib_index, FIB_PROTOCOL_IP4);
              if (vss)
@@ -311,7 +304,7 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
                  o->data[14] = vss->vss_type;  /* vss option type */
                  o->data[15 + id_len] = 152;   /* vss control suboption */
                  o->data[16 + id_len] = 0;     /* length */
-                 o->data[17 + id_len] = 0xFF;  /* "end-of-options" (0xFF) */
+                 o->data[17 + id_len] = DHCP_PACKET_OPTION_END;        /* "end-of-options" (0xFF) */
                  /* 5 bytes for suboption headers 151+len, 152+len and 0xFF */
                  o->length += id_len + 5;
                }
@@ -429,7 +422,6 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
 
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
-
   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
                               DHCP_PROXY_ERROR_RELAY_TO_CLIENT,
                               pkts_to_client);
@@ -535,24 +527,32 @@ dhcp_proxy_to_client_input (vlib_main_t * vm,
 
       if (1 /* dpm->insert_option_82 */ )
        {
-         dhcp_option_t *o = (dhcp_option_t *) h0->options;
-         dhcp_option_t *sub;
+         /* linearize needed to "unclone" and scan options */
+         int space_left = vlib_buffer_chain_linearize (vm, b0);
+         if ((b0->flags & VLIB_BUFFER_NEXT_PRESENT) != 0 || space_left < 0)
+           {
+             error0 = DHCP_PROXY_ERROR_PKT_TOO_BIG;
+             goto drop_packet;
+           }
+
+         dhcp_option_t *o = h0->options, *end =
+           (void *) vlib_buffer_get_tail (b0);
 
          /* Parse through TLVs looking for option 82.
             The circuit-ID is the FIB number we need
             to track down the client-facing interface */
 
-         while (o->option != 0xFF /* end of options */  &&
-                (u8 *) o <
-                (b0->data + b0->current_data + b0->current_length))
+         while (o->option != DHCP_PACKET_OPTION_END && o < end)
            {
              if (o->option == 82)
                {
                  u32 vss_exist = 0;
                  u32 vss_ctrl = 0;
-                 sub = (dhcp_option_t *) & o->data[0];
-                 while (sub->option != 0xFF /* end of options */  &&
-                        (u8 *) sub < (u8 *) (o + o->length))
+                 dhcp_option_t *sub = (dhcp_option_t *) & o->data[0];
+                 dhcp_option_t *subend =
+                   (dhcp_option_t *) (o->data + o->length);
+                 while (sub->option != DHCP_PACKET_OPTION_END
+                        && sub < subend)
                    {
                      /* If this is one of ours, it will have
                         total length 12, circuit-id suboption type,
@@ -576,8 +576,7 @@ dhcp_proxy_to_client_input (vlib_main_t * vm,
                        vss_exist = 1;
                      else if (sub->option == 152 && sub->length == 0)
                        vss_ctrl = 1;
-                     sub = (dhcp_option_t *)
-                       (((uword) sub) + (sub->length + 2));
+                     sub = (dhcp_option_t *) (sub->data + sub->length);
                    }
                  if (vss_ctrl && vss_exist)
                    vlib_node_increment_counter
@@ -585,7 +584,7 @@ dhcp_proxy_to_client_input (vlib_main_t * vm,
                       DHCP_PROXY_ERROR_OPTION_82_VSS_NOT_PROCESSED, 1);
 
                }
-             o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
+             o = (dhcp_option_t *) (o->data + o->length);
            }
        }
 
@@ -716,6 +715,7 @@ dhcp_proxy_to_client_input (vlib_main_t * vm,
          tr->sw_if_index = sw_if_index;
        }
     }
+
   return from_frame->n_vectors;
 }