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 <ksekera@cisco.com>
diff --git a/src/plugins/map/ip6_map.c b/src/plugins/map/ip6_map.c
index f14b880..96f81ef 100644
--- a/src/plugins/map/ip6_map.c
+++ b/src/plugins/map/ip6_map.c
@@ -25,7 +25,6 @@
 #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 @@
 	    }
 	  else if (ip60->protocol == IP_PROTOCOL_IPV6_FRAGMENTATION)
 	    {
-	      next0 = IP6_MAP_NEXT_IP6_REASS;
+	      error0 = MAP_ERROR_FRAGMENTED;
 	    }
 	  else
 	    {
@@ -294,7 +293,7 @@
 	    }
 	  else if (ip61->protocol == IP_PROTOCOL_IPV6_FRAGMENTATION)
 	    {
-	      next1 = IP6_MAP_NEXT_IP6_REASS;
+	      error1 = MAP_ERROR_FRAGMENTED;
 	    }
 	  else
 	    {
@@ -474,7 +473,7 @@
 		   (((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 @@
 }
 
 
-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)
 {
@@ -659,150 +562,6 @@
 }
 
 /*
- * 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
  */
 static uword
@@ -1070,6 +829,7 @@
   .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 @@
 #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",
@@ -1100,23 +859,6 @@
 /* *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,
   .name = "ip6-map-post-ip4-reass",
diff --git a/src/plugins/map/map.api b/src/plugins/map/map.api
index f2a7f84..857a1c8 100644
--- a/src/plugins/map/map.api
+++ b/src/plugins/map/map.api
@@ -241,28 +241,6 @@
   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 @@
     @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 @@
   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 904f0e9..7225fc6 100644
--- a/src/plugins/map/map.c
+++ b/src/plugins/map/map.c
@@ -966,23 +966,6 @@
   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 @@
   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)
 {
@@ -1170,101 +1140,6 @@
 }
 
 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 @@
   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)
@@ -1694,21 +1250,6 @@
 /* *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 <lifetime-ms>] "
-                "[pool-size <pool-size>] [buffers <buffers>] "
-                "[ht-ratio <ht-ratio>]",
-  .function = map_params_reass_command_fn,
-};
-
-/*?
  * Set or copy the IP TOS/Traffic Class field
  *
  * @cliexpar
@@ -1907,19 +1448,6 @@
 };
 
 /*?
- * 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 @@
   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 8098a79..9581bd5 100644
--- a/src/plugins/map/map.h
+++ b/src/plugins/map/map.h
@@ -50,9 +50,6 @@
 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 @@
   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 @@
   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 @@
   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 @@
 
 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 ea3212a..418f6a0 100644
--- a/src/plugins/map/map_api.c
+++ b/src/plugins/map/map_api.c
@@ -24,6 +24,7 @@
 #include <vnet/ip/ip.h>
 #include <vnet/ip/reass/ip4_sv_reass.h>
 #include <vnet/ip/reass/ip6_sv_reass.h>
+#include <vnet/ip/reass/ip6_full_reass.h>
 #include <vnet/fib/fib_table.h>
 #include <vlibmemory/api.h>
 
@@ -330,102 +331,6 @@
   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 @@
   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 @@
   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 368cf51..5549d9f 100644
--- a/src/plugins/map/test/test_map.py
+++ b/src/plugins/map/test/test_map.py
@@ -7,7 +7,7 @@
 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
@@ -232,6 +232,29 @@
             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)
+            self.assertEqual(r[IP].dst, p[IP].dst)
+
         #
         # Pre-resolve. No API for this!!
         #
diff --git a/src/vnet/ip/reass/ip4_full_reass.c b/src/vnet/ip/reass/ip4_full_reass.c
index 176c01c..87a6772 100644
--- a/src/vnet/ip/reass/ip4_full_reass.c
+++ b/src/vnet/ip/reass/ip4_full_reass.c
@@ -185,6 +185,8 @@
   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 @@
   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 @@
 };
 /* *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 b8419eb..8c82989 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_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 92fab60..bba11e5 100644
--- a/src/vnet/ip/reass/ip6_full_reass.c
+++ b/src/vnet/ip/reass/ip6_full_reass.c
@@ -164,6 +164,8 @@
   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 @@
   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 @@
 };
 /* *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 c2463e0..546075b 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_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 */
 
 /*