summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEyal Bari <ebari@cisco.com>2018-11-05 13:29:25 +0200
committerJohn Lo <loj@cisco.com>2018-11-20 02:52:39 +0000
commitd3d424180fcd98561d656d5c2189e9e59ef2b2b9 (patch)
treea9288e7e960b97072de833fb3453303c1d242198
parentc7b03fe8f3fd2753d97ffdf8e7ebd85e2c527085 (diff)
dhcp4:(VPP-1483) linearize chained packets before handling
dhcp packets might (when flooded) arrive in chains of cloned buffers Change-Id: Ifddecd656b6a5d6ba8cd94184f5c021684e35548 Signed-off-by: Eyal Bari <ebari@cisco.com>
-rw-r--r--src/vlib/buffer.h2
-rw-r--r--src/vlib/buffer_funcs.h64
-rw-r--r--src/vnet/dhcp/dhcp4_packet.h25
-rw-r--r--src/vnet/dhcp/dhcp4_proxy_node.c82
4 files changed, 119 insertions, 54 deletions
diff --git a/src/vlib/buffer.h b/src/vlib/buffer.h
index 493d111041e..02b170907ba 100644
--- a/src/vlib/buffer.h
+++ b/src/vlib/buffer.h
@@ -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 */
diff --git a/src/vlib/buffer_funcs.h b/src/vlib/buffer_funcs.h
index d15ef5702f8..e8ccc86f1a9 100644
--- a/src/vlib/buffer_funcs.h
+++ b/src/vlib/buffer_funcs.h
@@ -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 */
/*
diff --git a/src/vnet/dhcp/dhcp4_packet.h b/src/vnet/dhcp/dhcp4_packet.h
index 9fbeb02fb2b..3076dd9529d 100644
--- a/src/vnet/dhcp/dhcp4_packet.h
+++ b/src/vnet/dhcp/dhcp4_packet.h
@@ -21,6 +21,17 @@
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 */
u8 hardware_type; /* 1 = ethernet */
u8 hardware_address_length;
@@ -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 */
diff --git a/src/vnet/dhcp/dhcp4_proxy_node.c b/src/vnet/dhcp/dhcp4_proxy_node.c
index 99e5a739f74..c1ae5141ce3 100644
--- a/src/vnet/dhcp/dhcp4_proxy_node.c
+++ b/src/vnet/dhcp/dhcp4_proxy_node.c
@@ -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;
}