ip: translate fragmented icmp to fragmented icmp6

The first translated ICMPv6 packet of a fragmented ICMP message does
not have a IPv6 fragment header. All subsequent have.

With this commit, add a IPv6 fragment header to the first translated
ICMPv6 packet.

Type: fix

Change-Id: Id89409ce7273cbeed801e2e18a09d3e7c3c4e4bc
Signed-off-by: Alexander Chernavin <achernavin@netgate.com>
diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h
index e78985b..a6d87f1 100644
--- a/src/vnet/ip/ip4_to_ip6.h
+++ b/src/vnet/ip/ip4_to_ip6.h
@@ -226,8 +226,10 @@
   icmp46_header_t *icmp;
   ip_csum_t csum;
   ip6_frag_hdr_t *inner_frag;
+  ip6_frag_hdr_t *outer_frag= NULL;
   u32 inner_frag_id;
   u32 inner_frag_offset;
+  u32 outer_frag_id;
   u8 inner_frag_more;
   u16 *inner_L4_checksum = 0;
   int rv;
@@ -397,8 +399,20 @@
     }
   else
     {
-      vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
-      ip6 = vlib_buffer_get_current (p);
+      if (PREDICT_FALSE (ip4->flags_and_fragment_offset &
+			 clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
+	{
+	  vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) -
+			       sizeof (*outer_frag));
+	  ip6 = vlib_buffer_get_current (p);
+	  outer_frag = (ip6_frag_hdr_t *) (ip6 + 1);
+	  outer_frag_id = frag_id_4to6 (ip4->fragment_id);
+	}
+      else
+	{
+	  vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
+	  ip6 = vlib_buffer_get_current (p);
+	}
       ip6->payload_length =
 	clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
 			      sizeof (*ip4));
@@ -414,6 +428,17 @@
   if ((rv = fn (p, ip4, ip6, ctx)) != 0)
     return rv;
 
+  if (PREDICT_FALSE (outer_frag != NULL))
+    {
+      outer_frag->next_hdr = ip6->protocol;
+      outer_frag->identification = outer_frag_id;
+      outer_frag->rsv = 0;
+      outer_frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1);
+      ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
+      ip6->payload_length = u16_net_add (ip6->payload_length,
+					 sizeof (*outer_frag));
+    }
+
   //Truncate when ICMPv6 error message exceeds the minimal IPv6 MTU
   if (p->current_length > 1280 && icmp->type < 128)
     {