From: Eyal Bari Date: Mon, 5 Nov 2018 11:29:25 +0000 (+0200) Subject: dhcp4:(VPP-1483) linearize chained packets before handling X-Git-Tag: v19.04-rc0~378 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=d3d424180fcd98561d656d5c2189e9e59ef2b2b9 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 --- 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 @@ -19,6 +19,17 @@ */ #include +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 */ 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; }