From 7b2e9fb1a8f389fa7b88fcbaf3356cbdae254250 Mon Sep 17 00:00:00 2001 From: Klement Sekera Date: Tue, 1 Oct 2019 13:00:22 +0000 Subject: [PATCH] map: use ip6-full-reassembly instead of own code Remove map's implementation of reassembly and use common ip6-full-reassembly functionality. This makes it easier to maintain by removing duplicate code/functionality. Type: refactor Change-Id: I430e888b704e28c100a9ce075d1460cb529e4676 Signed-off-by: Klement Sekera --- src/plugins/map/ip6_map.c | 266 +------------------- src/plugins/map/map.api | 30 --- src/plugins/map/map.c | 484 ------------------------------------- src/plugins/map/map.h | 99 -------- src/plugins/map/map_api.c | 104 +------- src/plugins/map/test/test_map.py | 25 +- src/vnet/ip/reass/ip4_full_reass.c | 32 +++ src/vnet/ip/reass/ip4_full_reass.h | 3 + src/vnet/ip/reass/ip6_full_reass.c | 32 +++ src/vnet/ip/reass/ip6_full_reass.h | 3 + 10 files changed, 100 insertions(+), 978 deletions(-) diff --git a/src/plugins/map/ip6_map.c b/src/plugins/map/ip6_map.c index f14b880de3f..96f81efc1cb 100644 --- a/src/plugins/map/ip6_map.c +++ b/src/plugins/map/ip6_map.c @@ -25,7 +25,6 @@ enum ip6_map_next_e #ifdef MAP_SKIP_IP6_LOOKUP IP6_MAP_NEXT_IP4_REWRITE, #endif - IP6_MAP_NEXT_IP6_REASS, IP6_MAP_NEXT_IP4_REASS, IP6_MAP_NEXT_IP4_FRAGMENT, IP6_MAP_NEXT_IP6_ICMP_RELAY, @@ -267,7 +266,7 @@ ip6_map (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) } else if (ip60->protocol == IP_PROTOCOL_IPV6_FRAGMENTATION) { - next0 = IP6_MAP_NEXT_IP6_REASS; + error0 = MAP_ERROR_FRAGMENTED; } else { @@ -294,7 +293,7 @@ ip6_map (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) } else if (ip61->protocol == IP_PROTOCOL_IPV6_FRAGMENTATION) { - next1 = IP6_MAP_NEXT_IP6_REASS; + error1 = MAP_ERROR_FRAGMENTED; } else { @@ -474,7 +473,7 @@ ip6_map (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) (((ip6_frag_hdr_t *) (ip60 + 1))->next_hdr == IP_PROTOCOL_IP_IN_IP)) { - next0 = IP6_MAP_NEXT_IP6_REASS; + error0 = MAP_ERROR_FRAGMENTED; } else { @@ -553,102 +552,6 @@ ip6_map (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) } -static_always_inline void -ip6_map_ip6_reass_prepare (vlib_main_t * vm, vlib_node_runtime_t * node, - map_ip6_reass_t * r, u32 ** fragments_ready, - u32 ** fragments_to_drop) -{ - ip4_header_t *ip40; - ip6_header_t *ip60; - ip6_frag_hdr_t *frag0; - vlib_buffer_t *p0; - - if (!r->ip4_header.ip_version_and_header_length) - return; - - //The IP header is here, we need to check for packets - //that can be forwarded - int i; - for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++) - { - if (r->fragments[i].pi == ~0 || - ((!r->fragments[i].next_data_len) - && (r->fragments[i].next_data_offset != (0xffff)))) - continue; - - p0 = vlib_get_buffer (vm, r->fragments[i].pi); - ip60 = vlib_buffer_get_current (p0); - frag0 = (ip6_frag_hdr_t *) (ip60 + 1); - ip40 = (ip4_header_t *) (frag0 + 1); - - if (ip6_frag_hdr_offset (frag0)) - { - //Not first fragment, add the IPv4 header - clib_memcpy_fast (ip40, &r->ip4_header, 20); - } - -#ifdef MAP_IP6_REASS_COUNT_BYTES - r->forwarded += - clib_net_to_host_u16 (ip60->payload_length) - sizeof (*frag0); -#endif - - if (ip6_frag_hdr_more (frag0)) - { - //Not last fragment, we copy end of next - clib_memcpy_fast (u8_ptr_add (ip60, p0->current_length), - r->fragments[i].next_data, 20); - p0->current_length += 20; - ip60->payload_length = u16_net_add (ip60->payload_length, 20); - } - - if (!ip4_is_fragment (ip40)) - { - ip40->fragment_id = frag_id_6to4 (frag0->identification); - ip40->flags_and_fragment_offset = - clib_host_to_net_u16 (ip6_frag_hdr_offset (frag0)); - } - else - { - ip40->flags_and_fragment_offset = - clib_host_to_net_u16 (ip4_get_fragment_offset (ip40) + - ip6_frag_hdr_offset (frag0)); - } - - if (ip6_frag_hdr_more (frag0)) - ip40->flags_and_fragment_offset |= - clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS); - - ip40->length = - clib_host_to_net_u16 (p0->current_length - sizeof (*ip60) - - sizeof (*frag0)); - ip40->checksum = ip4_header_checksum (ip40); - - if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED)) - { - map_ip6_map_ip6_reass_trace_t *tr = - vlib_add_trace (vm, node, p0, sizeof (*tr)); - tr->offset = ip4_get_fragment_offset (ip40); - tr->frag_len = clib_net_to_host_u16 (ip40->length) - sizeof (*ip40); - tr->out = 1; - } - - vec_add1 (*fragments_ready, r->fragments[i].pi); - r->fragments[i].pi = ~0; - r->fragments[i].next_data_len = 0; - r->fragments[i].next_data_offset = 0; - map_main.ip6_reass_buffered_counter--; - - //TODO: Best solution would be that ip6_map handles extension headers - // and ignores atomic fragment. But in the meantime, let's just copy the header. - - u8 protocol = frag0->next_hdr; - memmove (u8_ptr_add (ip40, -sizeof (*ip60)), ip60, sizeof (*ip60)); - ((ip6_header_t *) u8_ptr_add (ip40, -sizeof (*ip60)))->protocol = - protocol; - vlib_buffer_advance (p0, sizeof (*frag0)); - } -} - void map_ip6_drop_pi (u32 pi) { @@ -658,150 +561,6 @@ map_ip6_drop_pi (u32 pi) vlib_set_next_frame_buffer (vm, n, IP6_MAP_IP6_REASS_NEXT_DROP, pi); } -/* - * ip6_reass - * TODO: We should count the number of successfully - * transmitted fragment bytes and compare that to the last fragment - * offset such that we can free the reassembly structure when all fragments - * have been forwarded. - */ -static uword -ip6_map_ip6_reass (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - u32 n_left_from, *from, next_index, *to_next, n_left_to_next; - vlib_node_runtime_t *error_node = - vlib_node_get_runtime (vm, ip6_map_ip6_reass_node.index); - u32 *fragments_to_drop = NULL; - u32 *fragments_ready = NULL; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - while (n_left_from > 0) - { - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - /* Single loop */ - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 pi0; - vlib_buffer_t *p0; - u8 error0 = MAP_ERROR_NONE; - ip6_header_t *ip60; - ip6_frag_hdr_t *frag0; - u16 offset; - u16 next_offset; - u16 frag_len; - - pi0 = to_next[0] = from[0]; - from += 1; - n_left_from -= 1; - to_next += 1; - n_left_to_next -= 1; - - p0 = vlib_get_buffer (vm, pi0); - ip60 = vlib_buffer_get_current (p0); - frag0 = (ip6_frag_hdr_t *) (ip60 + 1); - offset = - clib_host_to_net_u16 (frag0->fragment_offset_and_more) & (~7); - frag_len = - clib_net_to_host_u16 (ip60->payload_length) - sizeof (*frag0); - next_offset = - ip6_frag_hdr_more (frag0) ? (offset + frag_len) : (0xffff); - - //FIXME: Support other extension headers, maybe - - if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED)) - { - map_ip6_map_ip6_reass_trace_t *tr = - vlib_add_trace (vm, node, p0, sizeof (*tr)); - tr->offset = offset; - tr->frag_len = frag_len; - tr->out = 0; - } - - map_ip6_reass_lock (); - map_ip6_reass_t *r = - map_ip6_reass_get (&ip60->src_address, &ip60->dst_address, - frag0->identification, frag0->next_hdr, - &fragments_to_drop); - //FIXME: Use better error codes - if (PREDICT_FALSE (!r)) - { - // Could not create a caching entry - error0 = MAP_ERROR_FRAGMENT_MEMORY; - } - else if (PREDICT_FALSE ((frag_len <= 20 && - (ip6_frag_hdr_more (frag0) || (!offset))))) - { - //Very small fragment are restricted to the last one and - //can't be the first one - error0 = MAP_ERROR_FRAGMENT_MALFORMED; - } - else - if (map_ip6_reass_add_fragment - (r, pi0, offset, next_offset, (u8 *) (frag0 + 1), frag_len)) - { - map_ip6_reass_free (r, &fragments_to_drop); - error0 = MAP_ERROR_FRAGMENT_MEMORY; - } - else - { -#ifdef MAP_IP6_REASS_COUNT_BYTES - if (!ip6_frag_hdr_more (frag0)) - r->expected_total = offset + frag_len; -#endif - ip6_map_ip6_reass_prepare (vm, node, r, &fragments_ready, - &fragments_to_drop); -#ifdef MAP_IP6_REASS_COUNT_BYTES - if (r->forwarded >= r->expected_total) - map_ip6_reass_free (r, &fragments_to_drop); -#endif - } - map_ip6_reass_unlock (); - - if (error0 == MAP_ERROR_NONE) - { - if (frag_len > 20) - { - //Dequeue the packet - n_left_to_next++; - to_next--; - } - else - { - //All data from that packet was copied no need to keep it, but this is not an error - p0->error = error_node->errors[MAP_ERROR_NONE]; - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - pi0, - IP6_MAP_IP6_REASS_NEXT_DROP); - } - } - else - { - p0->error = error_node->errors[error0]; - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, - n_left_to_next, pi0, - IP6_MAP_IP6_REASS_NEXT_DROP); - } - } - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - map_send_all_to_node (vm, fragments_ready, node, - &error_node->errors[MAP_ERROR_NONE], - IP6_MAP_IP6_REASS_NEXT_IP6_MAP); - map_send_all_to_node (vm, fragments_to_drop, node, - &error_node->errors[MAP_ERROR_FRAGMENT_DROPPED], - IP6_MAP_IP6_REASS_NEXT_DROP); - - vec_free (fragments_to_drop); - vec_free (fragments_ready); - return frame->n_vectors; -} - /* * ip6_map_post_ip4_reass */ @@ -1070,6 +829,7 @@ VNET_FEATURE_INIT (ip6_map_feature, static) = .arc_name = "ip6-unicast", .node_name = "ip6-map", .runs_before = VNET_FEATURES ("ip6-flow-classify"), + .runs_after = VNET_FEATURES ("ip6-full-reassembly-feature"), }; VLIB_REGISTER_NODE(ip6_map_node) = { @@ -1088,7 +848,6 @@ VLIB_REGISTER_NODE(ip6_map_node) = { #ifdef MAP_SKIP_IP6_LOOKUP [IP6_MAP_NEXT_IP4_REWRITE] = "ip4-load-balance", #endif - [IP6_MAP_NEXT_IP6_REASS] = "ip6-map-ip6-reass", [IP6_MAP_NEXT_IP4_REASS] = "ip4-sv-reassembly-custom-next", [IP6_MAP_NEXT_IP4_FRAGMENT] = "ip4-frag", [IP6_MAP_NEXT_IP6_ICMP_RELAY] = "ip6-map-icmp-relay", @@ -1099,23 +858,6 @@ VLIB_REGISTER_NODE(ip6_map_node) = { }; /* *INDENT-ON* */ -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE(ip6_map_ip6_reass_node) = { - .function = ip6_map_ip6_reass, - .name = "ip6-map-ip6-reass", - .vector_size = sizeof(u32), - .format_trace = format_ip6_map_ip6_reass_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = MAP_N_ERROR, - .error_strings = map_error_strings, - .n_next_nodes = IP6_MAP_IP6_REASS_N_NEXT, - .next_nodes = { - [IP6_MAP_IP6_REASS_NEXT_IP6_MAP] = "ip6-map", - [IP6_MAP_IP6_REASS_NEXT_DROP] = "error-drop", - }, -}; -/* *INDENT-ON* */ - /* *INDENT-OFF* */ VLIB_REGISTER_NODE(ip6_map_post_ip4_reass_node) = { .function = ip6_map_post_ip4_reass, diff --git a/src/plugins/map/map.api b/src/plugins/map/map.api index f2a7f84de98..857a1c8fcb0 100644 --- a/src/plugins/map/map.api +++ b/src/plugins/map/map.api @@ -241,28 +241,6 @@ autoreply define map_param_add_del_pre_resolve vl_api_ip6_address_t ip6_nh_address; }; - -/** \brief Set MAP reassembly parameters - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_ip6 - 1 = params apply to IPv6, 0 = params apply to IPv4 - @param lifetime_ms - reassembly valid lifetime, or ~0 - @param pool_size - max number of reassemblies, or ~0 - @param buffers - max number of reassembly buffers, or ~0 - @param ht_ratio - hash-table size factor, or ~0 -*/ -autoreply define map_param_set_reassembly -{ - u32 client_index; - u32 context; - bool is_ip6; - u16 lifetime_ms; - u16 pool_size; - u32 buffers; - f64 ht_ratio; -}; - - /** \brief Set MAP security-check parameters @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -326,10 +304,6 @@ define map_param_get @param icmp6_enable_unreachable - 1 = send ICMP unreachable err msgs @param ip4_nh_address - direct IP4 next-hop address @param ip6_nh_address - direct IP6 next-hop address - @param ip6_lifetime_ms - max number of reassemblies, or ~0 - @param ip6_pool_size - max number of reassemblies, or ~0 - @param ip6_buffers - max number of reassembly buffers, or ~0 - @param ip6_ht_ratio - hash-table size factor, or ~0 @param sec_check_enable - 1=enable security check on first inbound packet @param sec_check_fragments - 1=enable check on (subsequent) fragments too @param tc_copy - 1 = copy packet class/TOS field, 0 = use class instead @@ -349,10 +323,6 @@ define map_param_get_reply u16 ip4_pool_size; u32 ip4_buffers; f64 ip4_ht_ratio; - u16 ip6_lifetime_ms; - u16 ip6_pool_size; - u32 ip6_buffers; - f64 ip6_ht_ratio; bool sec_check_enable; bool sec_check_fragments; bool tc_copy; diff --git a/src/plugins/map/map.c b/src/plugins/map/map.c index 904f0e9fd5e..7225fc62f81 100644 --- a/src/plugins/map/map.c +++ b/src/plugins/map/map.c @@ -966,23 +966,6 @@ format_map_domain (u8 * s, va_list * args) return s; } -static u8 * -format_map_ip6_reass (u8 * s, va_list * args) -{ - map_main_t *mm = &map_main; - map_ip6_reass_t *r = va_arg (*args, map_ip6_reass_t *); - map_ip6_reass_key_t *k = &r->key; - f64 now = vlib_time_now (mm->vlib_main); - f64 lifetime = (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000); - f64 dt = (r->ts + lifetime > now) ? (r->ts + lifetime - now) : -1; - s = format (s, - "ip6-reass src=%U dst=%U protocol=%d identifier=%d lifetime=%.3lf\n", - format_ip6_address, &k->src.as_u8, format_ip6_address, - &k->dst.as_u8, k->protocol, - clib_net_to_host_u32 (k->fragment_id), dt); - return s; -} - static clib_error_t * show_map_domain_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -1041,19 +1024,6 @@ done: return error; } -static clib_error_t * -show_map_fragments_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - map_main_t *mm = &map_main; - map_ip6_reass_t *f6; - - /* *INDENT-OFF* */ - pool_foreach(f6, mm->ip6_reass_pool, ({vlib_cli_output (vm, "%U", format_map_ip6_reass, f6);})); - /* *INDENT-ON* */ - return (0); -} - u64 map_error_counter_get (u32 node_index, map_error_t map_error) { @@ -1169,101 +1139,6 @@ show_map_stats_command_fn (vlib_main_t * vm, unformat_input_t * input, return 0; } -static clib_error_t * -map_params_reass_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - u32 lifetime = ~0; - f64 ht_ratio = (MAP_IP6_REASS_CONF_HT_RATIO_MAX + 1); - u32 pool_size = ~0; - u64 buffers = ~(0ull); - u8 ip4 = 0, ip6 = 0; - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "lifetime %u", &lifetime)) - ; - else if (unformat (line_input, "ht-ratio %lf", &ht_ratio)) - ; - else if (unformat (line_input, "pool-size %u", &pool_size)) - ; - else if (unformat (line_input, "buffers %llu", &buffers)) - ; - else if (unformat (line_input, "ip4")) - ip4 = 1; - else if (unformat (line_input, "ip6")) - ip6 = 1; - else - { - unformat_free (line_input); - return clib_error_return (0, "invalid input"); - } - } - unformat_free (line_input); - - if (!ip4 && !ip6) - return clib_error_return (0, "must specify ip4 and/or ip6"); - - if (ip4) - { - return clib_error_return (0, - "ip4 reassembly no longer supported in map"); - } - - if (ip6) - { - if (pool_size != ~0 && pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX) - return clib_error_return (0, "invalid ip6-reass pool-size ( > %d)", - MAP_IP6_REASS_CONF_POOL_SIZE_MAX); - if (ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX) - return clib_error_return (0, "invalid ip6-reass ht-log2len ( > %d)", - MAP_IP6_REASS_CONF_HT_RATIO_MAX); - if (lifetime != ~0 && lifetime > MAP_IP6_REASS_CONF_LIFETIME_MAX) - return clib_error_return (0, "invalid ip6-reass lifetime ( > %d)", - MAP_IP6_REASS_CONF_LIFETIME_MAX); - if (buffers != ~(0ull) && buffers > MAP_IP6_REASS_CONF_BUFFERS_MAX) - return clib_error_return (0, "invalid ip6-reass buffers ( > %ld)", - MAP_IP6_REASS_CONF_BUFFERS_MAX); - } - - int rv; - u32 reass = 0, packets = 0; - rv = map_param_set_reassembly (!ip4, lifetime, pool_size, buffers, ht_ratio, - &reass, &packets); - - switch (rv) - { - case 0: - vlib_cli_output (vm, - "Note: destroyed-reassembly=%u , dropped-fragments=%u", - reass, packets); - break; - - case MAP_ERR_BAD_POOL_SIZE: - return clib_error_return (0, "Could not set reass pool-size"); - - case MAP_ERR_BAD_HT_RATIO: - return clib_error_return (0, "Could not set reass ht-log2len"); - - case MAP_ERR_BAD_LIFETIME: - return clib_error_return (0, "Could not set ip6-reass lifetime"); - - case MAP_ERR_BAD_BUFFERS: - return clib_error_return (0, "Could not set ip6-reass buffers"); - - case MAP_ERR_BAD_BUFFERS_TOO_LARGE: - return clib_error_return (0, - "Note: 'ip6-reass buffers' > pool-size * max-fragments-per-reassembly."); - } - - return 0; -} - - static clib_error_t * map_if_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -1333,325 +1208,6 @@ format_map_trace (u8 * s, va_list * args) return s; } -static_always_inline map_ip6_reass_t * -map_ip6_reass_lookup (map_ip6_reass_key_t * k, u32 bucket, f64 now) -{ - map_main_t *mm = &map_main; - u32 ri = mm->ip6_reass_hash_table[bucket]; - while (ri != MAP_REASS_INDEX_NONE) - { - map_ip6_reass_t *r = pool_elt_at_index (mm->ip6_reass_pool, ri); - if (now < r->ts + (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000) && - r->key.as_u64[0] == k->as_u64[0] && - r->key.as_u64[1] == k->as_u64[1] && - r->key.as_u64[2] == k->as_u64[2] && - r->key.as_u64[3] == k->as_u64[3] && - r->key.as_u64[4] == k->as_u64[4]) - return r; - ri = r->bucket_next; - } - return NULL; -} - -#define map_ip6_reass_pool_index(r) (r - map_main.ip6_reass_pool) - -void -map_ip6_reass_free (map_ip6_reass_t * r, u32 ** pi_to_drop) -{ - map_main_t *mm = &map_main; - int i; - for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++) - if (r->fragments[i].pi != ~0) - { - vec_add1 (*pi_to_drop, r->fragments[i].pi); - r->fragments[i].pi = ~0; - map_main.ip6_reass_buffered_counter--; - } - - // Unlink in hash bucket - map_ip6_reass_t *r2 = NULL; - u32 r2i = mm->ip6_reass_hash_table[r->bucket]; - while (r2i != map_ip6_reass_pool_index (r)) - { - ASSERT (r2i != MAP_REASS_INDEX_NONE); - r2 = pool_elt_at_index (mm->ip6_reass_pool, r2i); - r2i = r2->bucket_next; - } - if (r2) - { - r2->bucket_next = r->bucket_next; - } - else - { - mm->ip6_reass_hash_table[r->bucket] = r->bucket_next; - } - - // Unlink in list - if (r->fifo_next == map_ip6_reass_pool_index (r)) - { - //Single element in the list, list is now empty - mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE; - } - else - { - if (mm->ip6_reass_fifo_last == map_ip6_reass_pool_index (r)) //First element - mm->ip6_reass_fifo_last = r->fifo_prev; - pool_elt_at_index (mm->ip6_reass_pool, r->fifo_prev)->fifo_next = - r->fifo_next; - pool_elt_at_index (mm->ip6_reass_pool, r->fifo_next)->fifo_prev = - r->fifo_prev; - } - - // Free from pool if necessary - pool_put (mm->ip6_reass_pool, r); - mm->ip6_reass_allocated--; -} - -map_ip6_reass_t * -map_ip6_reass_get (ip6_address_t * src, ip6_address_t * dst, u32 fragment_id, - u8 protocol, u32 ** pi_to_drop) -{ - map_ip6_reass_t *r; - map_main_t *mm = &map_main; - map_ip6_reass_key_t k = { - .src = *src, - .dst = *dst, - .fragment_id = fragment_id, - .protocol = protocol - }; - - u32 h = 0; - int i; - -#ifdef clib_crc32c_uses_intrinsics - h = clib_crc32c ((u8 *) k.as_u32, 40); -#else - u64 tmp = - k.as_u64[0] ^ k.as_u64[1] ^ k.as_u64[2] ^ k.as_u64[3] ^ k.as_u64[4]; - h = clib_xxhash (tmp); -#endif - - h = h >> (32 - mm->ip6_reass_ht_log2len); - - f64 now = vlib_time_now (mm->vlib_main); - - //Cache garbage collection - while (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE) - { - map_ip6_reass_t *last = - pool_elt_at_index (mm->ip6_reass_pool, mm->ip6_reass_fifo_last); - if (last->ts + (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000) < now) - map_ip6_reass_free (last, pi_to_drop); - else - break; - } - - if ((r = map_ip6_reass_lookup (&k, h, now))) - return r; - - if (mm->ip6_reass_allocated >= mm->ip6_reass_conf_pool_size) - return NULL; - - pool_get (mm->ip6_reass_pool, r); - mm->ip6_reass_allocated++; - for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++) - { - r->fragments[i].pi = ~0; - r->fragments[i].next_data_len = 0; - r->fragments[i].next_data_offset = 0; - } - - u32 ri = map_ip6_reass_pool_index (r); - - //Link in new bucket - r->bucket = h; - r->bucket_next = mm->ip6_reass_hash_table[h]; - mm->ip6_reass_hash_table[h] = ri; - - //Link in fifo - if (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE) - { - r->fifo_next = - pool_elt_at_index (mm->ip6_reass_pool, - mm->ip6_reass_fifo_last)->fifo_next; - r->fifo_prev = mm->ip6_reass_fifo_last; - pool_elt_at_index (mm->ip6_reass_pool, r->fifo_prev)->fifo_next = ri; - pool_elt_at_index (mm->ip6_reass_pool, r->fifo_next)->fifo_prev = ri; - } - else - { - r->fifo_next = r->fifo_prev = ri; - mm->ip6_reass_fifo_last = ri; - } - - //Set other fields - r->ts = now; - r->key = k; - r->ip4_header.ip_version_and_header_length = 0; -#ifdef MAP_IP6_REASS_COUNT_BYTES - r->expected_total = 0xffff; - r->forwarded = 0; -#endif - return r; -} - -int -map_ip6_reass_add_fragment (map_ip6_reass_t * r, u32 pi, - u16 data_offset, u16 next_data_offset, - u8 * data_start, u16 data_len) -{ - map_ip6_fragment_t *f = NULL, *prev_f = NULL; - u16 copied_len = (data_len > 20) ? 20 : data_len; - - if (map_main.ip6_reass_buffered_counter >= map_main.ip6_reass_conf_buffers) - return -1; - - //Lookup for fragments for the current buffer - //and the one before that - int i; - for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++) - { - if (data_offset && r->fragments[i].next_data_offset == data_offset) - { - prev_f = &r->fragments[i]; // This is buffer for previous packet - } - else if (r->fragments[i].next_data_offset == next_data_offset) - { - f = &r->fragments[i]; // This is a buffer for the current packet - } - else if (r->fragments[i].next_data_offset == 0) - { //Available - if (f == NULL) - f = &r->fragments[i]; - else if (prev_f == NULL) - prev_f = &r->fragments[i]; - } - } - - if (!f || f->pi != ~0) - return -1; - - if (data_offset) - { - if (!prev_f) - return -1; - - clib_memcpy_fast (prev_f->next_data, data_start, copied_len); - prev_f->next_data_len = copied_len; - prev_f->next_data_offset = data_offset; - } - else - { - if (((ip4_header_t *) data_start)->ip_version_and_header_length != 0x45) - return -1; - - if (r->ip4_header.ip_version_and_header_length == 0) - clib_memcpy_fast (&r->ip4_header, data_start, sizeof (ip4_header_t)); - } - - if (data_len > 20) - { - f->next_data_offset = next_data_offset; - f->pi = pi; - map_main.ip6_reass_buffered_counter++; - } - return 0; -} - -u8 -map_get_ht_log2len (f32 ht_ratio, u16 pool_size) -{ - u32 desired_size = (u32) (pool_size * ht_ratio); - u8 i; - for (i = 1; i < 31; i++) - if ((1 << i) >= desired_size) - return i; - return 4; -} - -void -map_ip6_reass_reinit (u32 * trashed_reass, u32 * dropped_packets) -{ - map_main_t *mm = &map_main; - if (dropped_packets) - *dropped_packets = mm->ip6_reass_buffered_counter; - if (trashed_reass) - *trashed_reass = mm->ip6_reass_allocated; - int i; - if (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE) - { - u16 ri = mm->ip6_reass_fifo_last; - do - { - map_ip6_reass_t *r = pool_elt_at_index (mm->ip6_reass_pool, ri); - for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++) - if (r->fragments[i].pi != ~0) - map_ip6_drop_pi (r->fragments[i].pi); - - ri = r->fifo_next; - pool_put (mm->ip6_reass_pool, r); - } - while (ri != mm->ip6_reass_fifo_last); - mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE; - } - - vec_free (mm->ip6_reass_hash_table); - vec_resize (mm->ip6_reass_hash_table, 1 << mm->ip6_reass_ht_log2len); - for (i = 0; i < (1 << mm->ip6_reass_ht_log2len); i++) - mm->ip6_reass_hash_table[i] = MAP_REASS_INDEX_NONE; - pool_free (mm->ip6_reass_pool); - pool_alloc (mm->ip6_reass_pool, mm->ip6_reass_conf_pool_size); - - mm->ip6_reass_allocated = 0; - mm->ip6_reass_buffered_counter = 0; -} - -int -map_ip6_reass_conf_ht_ratio (f32 ht_ratio, u32 * trashed_reass, - u32 * dropped_packets) -{ - map_main_t *mm = &map_main; - if (ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX) - return -1; - - map_ip6_reass_lock (); - mm->ip6_reass_conf_ht_ratio = ht_ratio; - mm->ip6_reass_ht_log2len = - map_get_ht_log2len (ht_ratio, mm->ip6_reass_conf_pool_size); - map_ip6_reass_reinit (trashed_reass, dropped_packets); - map_ip6_reass_unlock (); - return 0; -} - -int -map_ip6_reass_conf_pool_size (u16 pool_size, u32 * trashed_reass, - u32 * dropped_packets) -{ - map_main_t *mm = &map_main; - if (pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX) - return -1; - - map_ip6_reass_lock (); - mm->ip6_reass_conf_pool_size = pool_size; - map_ip6_reass_reinit (trashed_reass, dropped_packets); - map_ip6_reass_unlock (); - return 0; -} - -int -map_ip6_reass_conf_lifetime (u16 lifetime_ms) -{ - map_main.ip6_reass_conf_lifetime_ms = lifetime_ms; - return 0; -} - -int -map_ip6_reass_conf_buffers (u32 buffers) -{ - map_main.ip6_reass_conf_buffers = buffers; - return 0; -} - static clib_error_t * map_tcp_mss_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -1693,21 +1249,6 @@ done: /* *INDENT-OFF* */ -/*? - * Configure MAP reassembly behaviour - * - * @cliexpar - * @cliexstart{map params reassembly} - * @cliexend - ?*/ -VLIB_CLI_COMMAND(map_ip4_reass_lifetime_command, static) = { - .path = "map params reassembly", - .short_help = "map params reassembly [ip4 | ip6] [lifetime ] " - "[pool-size ] [buffers ] " - "[ht-ratio ]", - .function = map_params_reass_command_fn, -}; - /*? * Set or copy the IP TOS/Traffic Class field * @@ -1906,19 +1447,6 @@ VLIB_CLI_COMMAND(show_map_stats_command, static) = { .function = show_map_stats_command_fn, }; -/*? - * Show MAP fragmentation information - * - * @cliexpar - * @cliexstart{show map fragments} - * @cliexend - ?*/ -VLIB_CLI_COMMAND(show_map_fragments_command, static) = { - .path = "show map fragments", - .short_help = "show map fragments", - .function = show_map_fragments_command_fn, -}; - /*? * Enable MAP processing on interface (input feature) * @@ -1983,18 +1511,6 @@ map_init (vlib_main_t * vm) mm->icmp_relayed.stat_segment_name = "/map/icmp-relayed"; /* IP6 virtual reassembly */ - mm->ip6_reass_hash_table = 0; - mm->ip6_reass_pool = 0; - clib_spinlock_init (&mm->ip6_reass_lock); - mm->ip6_reass_conf_ht_ratio = MAP_IP6_REASS_HT_RATIO_DEFAULT; - mm->ip6_reass_conf_lifetime_ms = MAP_IP6_REASS_LIFETIME_DEFAULT; - mm->ip6_reass_conf_pool_size = MAP_IP6_REASS_POOL_SIZE_DEFAULT; - mm->ip6_reass_conf_buffers = MAP_IP6_REASS_BUFFERS_DEFAULT; - mm->ip6_reass_ht_log2len = - map_get_ht_log2len (mm->ip6_reass_conf_ht_ratio, - mm->ip6_reass_conf_pool_size); - mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE; - map_ip6_reass_reinit (NULL, NULL); #ifdef MAP_SKIP_IP6_LOOKUP fib_node_register_type (FIB_NODE_TYPE_MAP_E, &map_vft); diff --git a/src/plugins/map/map.h b/src/plugins/map/map.h index 8098a798aee..9581bd50fac 100644 --- a/src/plugins/map/map.h +++ b/src/plugins/map/map.h @@ -50,9 +50,6 @@ int map_param_set_fragmentation (bool inner, bool ignore_df); int map_param_set_icmp (ip4_address_t * ip4_err_relay_src); int map_param_set_icmp6 (u8 enable_unreachable); void map_pre_resolve (ip4_address_t * ip4, ip6_address_t * ip6, bool is_del); -int map_param_set_reassembly (bool is_ipv6, u16 lifetime_ms, u16 pool_size, - u32 buffers, f64 ht_ratio, u32 * reass, - u32 * packets); int map_param_set_security_check (bool enable, bool fragments); int map_param_set_traffic_class (bool copy, u8 tc); int map_param_set_tcp (u16 tcp_mss); @@ -65,15 +62,6 @@ typedef enum MAP_DOMAIN_RFC6052 = 1 << 2, } __attribute__ ((__packed__)) map_domain_flags_e; -#define MAP_IP6_REASS_LIFETIME_DEFAULT (100) /* ms */ -#define MAP_IP6_REASS_HT_RATIO_DEFAULT (1.0) -#define MAP_IP6_REASS_POOL_SIZE_DEFAULT 1024 // Number of reassembly structures -#define MAP_IP6_REASS_BUFFERS_DEFAULT 2048 - -#define MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY 5 - -#define MAP_IP6_REASS_COUNT_BYTES - //#define IP6_MAP_T_OVERRIDE_TOS 0 /* @@ -136,46 +124,6 @@ typedef enum MAP_N_DOMAIN_COUNTER } map_domain_counter_t; -/* - * main_main_t - */ -/* *INDENT-OFF* */ -typedef union { - CLIB_PACKED (struct { - ip6_address_t src; - ip6_address_t dst; - u32 fragment_id; - u8 protocol; - }); - u64 as_u64[5]; - u32 as_u32[10]; -} map_ip6_reass_key_t; -/* *INDENT-ON* */ - -typedef struct -{ - u32 pi; //Cached packet or ~0 - u16 next_data_offset; //The data offset of the additional 20 bytes or ~0 - u8 next_data_len; //Number of bytes ready to be copied (20 if not last fragment) - u8 next_data[20]; //The 20 additional bytes -} map_ip6_fragment_t; - -typedef struct -{ - map_ip6_reass_key_t key; - f64 ts; -#ifdef MAP_IP6_REASS_COUNT_BYTES - u16 expected_total; - u16 forwarded; -#endif - u16 bucket; //What hash bucket this element is linked in - u16 bucket_next; - u16 fifo_prev; - u16 fifo_next; - ip4_header_t ip4_header; - map_ip6_fragment_t fragments[MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY]; -} map_ip6_reass_t; - #ifdef MAP_SKIP_IP6_LOOKUP /** * A pre-resolved next-hop @@ -245,26 +193,6 @@ typedef struct bool frag_inner; /* Inner or outer fragmentation */ bool frag_ignore_df; /* Fragment (outer) packet even if DF is set */ - /* - * IPv6 decap reassembly - */ - /* Configuration */ - f32 ip6_reass_conf_ht_ratio; //Size of ht is 2^ceil(log2(ratio*pool_size)) - u16 ip6_reass_conf_pool_size; //Max number of allocated reass structures - u16 ip6_reass_conf_lifetime_ms; //Time a reassembly struct is considered valid in ms - u32 ip6_reass_conf_buffers; //Maximum number of buffers used by ip6 reassembly - - /* Runtime */ - map_ip6_reass_t *ip6_reass_pool; - u8 ip6_reass_ht_log2len; //Hash table size is 2^log2len - u16 ip6_reass_allocated; - u16 *ip6_reass_hash_table; - u16 ip6_reass_fifo_last; - clib_spinlock_t ip6_reass_lock; - - /* Counters */ - u32 ip6_reass_buffered_counter; - /* Graph node state */ uword *bm_trans_enabled_by_sw_if; uword *bm_encap_enabled_by_sw_if; @@ -445,35 +373,8 @@ ip6_map_get_domain (ip6_address_t * addr, u32 * map_domain_index, u8 * error) clib_error_t *map_plugin_api_hookup (vlib_main_t * vm); -map_ip6_reass_t *map_ip6_reass_get (ip6_address_t * src, ip6_address_t * dst, - u32 fragment_id, u8 protocol, - u32 ** pi_to_drop); -void map_ip6_reass_free (map_ip6_reass_t * r, u32 ** pi_to_drop); - -#define map_ip6_reass_lock() clib_spinlock_lock (&map_main.ip6_reass_lock) -#define map_ip6_reass_unlock() clib_spinlock_unlock (&map_main.ip6_reass_lock) - -int -map_ip6_reass_add_fragment (map_ip6_reass_t * r, u32 pi, - u16 data_offset, u16 next_data_offset, - u8 * data_start, u16 data_len); - -void map_ip4_drop_pi (u32 pi); - void map_ip6_drop_pi (u32 pi); - -int map_ip6_reass_conf_ht_ratio (f32 ht_ratio, u32 * trashed_reass, - u32 * dropped_packets); -#define MAP_IP6_REASS_CONF_HT_RATIO_MAX 100 -int map_ip6_reass_conf_pool_size (u16 pool_size, u32 * trashed_reass, - u32 * dropped_packets); -#define MAP_IP6_REASS_CONF_POOL_SIZE_MAX (0xfeff) -int map_ip6_reass_conf_lifetime (u16 lifetime_ms); -#define MAP_IP6_REASS_CONF_LIFETIME_MAX 0xffff -int map_ip6_reass_conf_buffers (u32 buffers); -#define MAP_IP6_REASS_CONF_BUFFERS_MAX (0xffffffff) - /* * Supports prefix of 96 or 64 (with u-octet) */ diff --git a/src/plugins/map/map_api.c b/src/plugins/map/map_api.c index ea3212a4960..418f6a02a36 100644 --- a/src/plugins/map/map_api.c +++ b/src/plugins/map/map_api.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -330,102 +331,6 @@ static void REPLY_MACRO (VL_API_MAP_PARAM_ADD_DEL_PRE_RESOLVE_REPLY); } - -int -map_param_set_reassembly (bool is_ipv6, - u16 lifetime_ms, - u16 pool_size, - u32 buffers, - f64 ht_ratio, u32 * reass, u32 * packets) -{ - u32 ps_reass = 0, ps_packets = 0; - u32 ht_reass = 0, ht_packets = 0; - - if (is_ipv6) - { - if (pool_size != (u16) ~ 0) - { - if (pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX) - return MAP_ERR_BAD_POOL_SIZE; - if (map_ip6_reass_conf_pool_size - (pool_size, &ps_reass, &ps_packets)) - return MAP_ERR_BAD_POOL_SIZE; - } - - if (ht_ratio != (MAP_IP6_REASS_CONF_HT_RATIO_MAX + 1)) - { - if (ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX) - return MAP_ERR_BAD_HT_RATIO; - if (map_ip6_reass_conf_ht_ratio (ht_ratio, &ht_reass, &ht_packets)) - return MAP_ERR_BAD_HT_RATIO; - } - - if (lifetime_ms != (u16) ~ 0) - { - if (lifetime_ms > MAP_IP6_REASS_CONF_LIFETIME_MAX) - return MAP_ERR_BAD_LIFETIME; - if (map_ip6_reass_conf_lifetime (lifetime_ms)) - return MAP_ERR_BAD_LIFETIME; - } - - if (buffers != ~0) - { - if (buffers > MAP_IP6_REASS_CONF_BUFFERS_MAX) - return MAP_ERR_BAD_BUFFERS; - if (map_ip6_reass_conf_buffers (buffers)) - return MAP_ERR_BAD_BUFFERS; - } - - if (map_main.ip6_reass_conf_buffers > - map_main.ip6_reass_conf_pool_size * - MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY) - { - return MAP_ERR_BAD_BUFFERS_TOO_LARGE; - } - } - else - { - return MAP_ERR_UNSUPPORTED; - } - - if (reass) - *reass = ps_reass + ht_reass; - - if (packets) - *packets = ps_packets + ht_packets; - - return 0; -} - - -static void - vl_api_map_param_set_reassembly_t_handler - (vl_api_map_param_set_reassembly_t * mp) -{ - map_main_t *mm = &map_main; - vl_api_map_param_set_reassembly_reply_t *rmp; - u32 reass = 0, packets = 0; - int rv; - f64 ht_ratio; - - ht_ratio = (f64) clib_net_to_host_f64 (mp->ht_ratio); - if (ht_ratio == ~0) - ht_ratio = MAP_IP6_REASS_CONF_HT_RATIO_MAX + 1; - - rv = map_param_set_reassembly (mp->is_ip6, - clib_net_to_host_u16 (mp->lifetime_ms), - clib_net_to_host_u16 (mp->pool_size), - clib_net_to_host_u32 (mp->buffers), - ht_ratio, &reass, &packets); - - /* - * FIXME: Should the lost reass and packet counts be returned in the API? - */ - - REPLY_MACRO (VL_API_MAP_PARAM_SET_REASSEMBLY_REPLY); -} - - int map_param_set_security_check (bool enable, bool fragments) { @@ -530,12 +435,6 @@ vl_api_map_param_get_t_handler (vl_api_map_param_get_t * mp) clib_memset (&rmp->ip4_nh_address, 0, sizeof (rmp->ip4_nh_address)); clib_memset (&rmp->ip6_nh_address, 0, sizeof (rmp->ip6_nh_address)); - rmp->ip6_lifetime_ms = - clib_net_to_host_u16 (mm->ip6_reass_conf_lifetime_ms); - rmp->ip6_pool_size = clib_net_to_host_u16 (mm->ip6_reass_conf_pool_size); - rmp->ip6_buffers = clib_net_to_host_u32 (mm->ip6_reass_conf_buffers); - rmp->ip6_ht_ratio = clib_net_to_host_f64 (mm->ip6_reass_conf_ht_ratio); - rmp->sec_check_enable = mm->sec_check; rmp->sec_check_fragments = mm->sec_check_frag; @@ -573,6 +472,7 @@ map_if_enable_disable (bool is_enable, u32 sw_if_index, bool is_translation) if (is_translation == false) { ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, is_enable); + ip6_full_reass_enable_disable_with_refcnt (sw_if_index, is_enable); vnet_feature_enable_disable ("ip4-unicast", "ip4-map", sw_if_index, is_enable ? 1 : 0, 0, 0); vnet_feature_enable_disable ("ip6-unicast", "ip6-map", sw_if_index, diff --git a/src/plugins/map/test/test_map.py b/src/plugins/map/test/test_map.py index 368cf51092d..5549d9f0751 100644 --- a/src/plugins/map/test/test_map.py +++ b/src/plugins/map/test/test_map.py @@ -7,7 +7,7 @@ from ipaddress import IPv6Network, IPv4Network from framework import VppTestCase, VppTestRunner from vpp_ip import DpoProto from vpp_ip_route import VppIpRoute, VppRoutePath -from util import fragment_rfc791 +from util import fragment_rfc791, fragment_rfc8200 import scapy.compat from scapy.layers.l2 import Ether, Raw @@ -227,6 +227,29 @@ class TestMAP(VppTestCase): rx = self.pg0.get_capture(len(frags)) + for r in rx: + self.assertFalse(r.haslayer(IPv6)) + self.assertEqual(r[IP].src, p[IP].src) + self.assertEqual(r[IP].dst, p[IP].dst) + + # Verify that fragments pass even if ipv6 layer is fragmented + stream = (IPv6(dst='3000::1', src=map_translated_addr) / x + for x in frags) + + v6_stream = [ + Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / x + for i in range(len(frags)) + for x in fragment_rfc8200( + IPv6(dst='3000::1', src=map_translated_addr) / frags[i], + i, 200)] + + self.pg1.add_stream(v6_stream) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture(len(frags)) + for r in rx: self.assertFalse(r.haslayer(IPv6)) self.assertEqual(r[IP].src, p[IP].src) diff --git a/src/vnet/ip/reass/ip4_full_reass.c b/src/vnet/ip/reass/ip4_full_reass.c index 176c01c74fb..87a677208fb 100644 --- a/src/vnet/ip/reass/ip4_full_reass.c +++ b/src/vnet/ip/reass/ip4_full_reass.c @@ -185,6 +185,8 @@ typedef struct u32 fq_index; u32 fq_feature_index; + // reference count for enabling/disabling feature - per interface + u32 *feature_use_refcount_per_intf; } ip4_full_reass_main_t; extern ip4_full_reass_main_t ip4_full_reass_main; @@ -1448,6 +1450,7 @@ ip4_full_reass_init_function (vlib_main_t * vm) rm->fq_feature_index = vlib_frame_queue_main_init (ip4_full_reass_node_feature.index, 0); + rm->feature_use_refcount_per_intf = NULL; return error; } @@ -1793,6 +1796,35 @@ VLIB_REGISTER_NODE (ip4_full_reass_feature_handoff_node) = { }; /* *INDENT-ON* */ +#ifndef CLIB_MARCH_VARIANT +int +ip4_full_reass_enable_disable_with_refcnt (u32 sw_if_index, int is_enable) +{ + ip4_full_reass_main_t *rm = &ip4_full_reass_main; + vec_validate (rm->feature_use_refcount_per_intf, sw_if_index); + if (is_enable) + { + if (!rm->feature_use_refcount_per_intf[sw_if_index]) + { + ++rm->feature_use_refcount_per_intf[sw_if_index]; + return vnet_feature_enable_disable ("ip4-unicast", + "ip4-full-reassembly-feature", + sw_if_index, 1, 0, 0); + } + ++rm->feature_use_refcount_per_intf[sw_if_index]; + } + else + { + --rm->feature_use_refcount_per_intf[sw_if_index]; + if (!rm->feature_use_refcount_per_intf[sw_if_index]) + return vnet_feature_enable_disable ("ip4-unicast", + "ip4-full-reassembly-feature", + sw_if_index, 0, 0, 0); + } + return -1; +} +#endif + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/ip/reass/ip4_full_reass.h b/src/vnet/ip/reass/ip4_full_reass.h index b8419eb4492..8c82989086b 100644 --- a/src/vnet/ip/reass/ip4_full_reass.h +++ b/src/vnet/ip/reass/ip4_full_reass.h @@ -43,6 +43,9 @@ vnet_api_error_t ip4_full_reass_get (u32 * timeout_ms, u32 * max_reassemblies, vnet_api_error_t ip4_full_reass_enable_disable (u32 sw_if_index, u8 enable_disable); +int ip4_full_reass_enable_disable_with_refcnt (u32 sw_if_index, + int is_enable); + #endif /* __included_ip4_full_reass_h__ */ /* diff --git a/src/vnet/ip/reass/ip6_full_reass.c b/src/vnet/ip/reass/ip6_full_reass.c index 92fab60d337..bba11e52959 100644 --- a/src/vnet/ip/reass/ip6_full_reass.c +++ b/src/vnet/ip/reass/ip6_full_reass.c @@ -164,6 +164,8 @@ typedef struct u32 fq_index; u32 fq_feature_index; + // reference count for enabling/disabling feature - per interface + u32 *feature_use_refcount_per_intf; } ip6_full_reass_main_t; extern ip6_full_reass_main_t ip6_full_reass_main; @@ -1427,6 +1429,7 @@ ip6_full_reass_init_function (vlib_main_t * vm) rm->fq_feature_index = vlib_frame_queue_main_init (ip6_full_reass_node_feature.index, 0); + rm->feature_use_refcount_per_intf = NULL; return error; } @@ -1790,6 +1793,35 @@ VLIB_REGISTER_NODE (ip6_full_reassembly_feature_handoff_node) = { }; /* *INDENT-ON* */ +#ifndef CLIB_MARCH_VARIANT +int +ip6_full_reass_enable_disable_with_refcnt (u32 sw_if_index, int is_enable) +{ + ip6_full_reass_main_t *rm = &ip6_full_reass_main; + vec_validate (rm->feature_use_refcount_per_intf, sw_if_index); + if (is_enable) + { + if (!rm->feature_use_refcount_per_intf[sw_if_index]) + { + ++rm->feature_use_refcount_per_intf[sw_if_index]; + return vnet_feature_enable_disable ("ip6-unicast", + "ip6-full-reassembly-feature", + sw_if_index, 1, 0, 0); + } + ++rm->feature_use_refcount_per_intf[sw_if_index]; + } + else + { + --rm->feature_use_refcount_per_intf[sw_if_index]; + if (!rm->feature_use_refcount_per_intf[sw_if_index]) + return vnet_feature_enable_disable ("ip6-unicast", + "ip6-full-reassembly-feature", + sw_if_index, 0, 0, 0); + } + return -1; +} +#endif + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/ip/reass/ip6_full_reass.h b/src/vnet/ip/reass/ip6_full_reass.h index c2463e0e8f3..546075b04b4 100644 --- a/src/vnet/ip/reass/ip6_full_reass.h +++ b/src/vnet/ip/reass/ip6_full_reass.h @@ -43,6 +43,9 @@ vnet_api_error_t ip6_full_reass_get (u32 * timeout_ms, u32 * max_reassemblies, vnet_api_error_t ip6_full_reass_enable_disable (u32 sw_if_index, u8 enable_disable); +int ip6_full_reass_enable_disable_with_refcnt (u32 sw_if_index, + int is_enable); + #endif /* __included_ip6_full_reass_h */ /* -- 2.16.6