ip: translate fragmented icmp to fragmented icmp6 14/24714/2
authorAlexander Chernavin <achernavin@netgate.com>
Fri, 31 Jan 2020 14:19:49 +0000 (09:19 -0500)
committerOle Trøan <otroan@employees.org>
Tue, 4 Feb 2020 09:31:44 +0000 (09:31 +0000)
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>
src/plugins/map/test/test_map.py
src/vnet/ip/ip4_to_ip6.h

index 845d1d3..123fb54 100644 (file)
@@ -12,7 +12,8 @@ import scapy.compat
 from scapy.layers.l2 import Ether
 from scapy.packet import Raw
 from scapy.layers.inet import IP, UDP, ICMP, TCP
-from scapy.layers.inet6 import IPv6, ICMPv6TimeExceeded, IPv6ExtHdrFragment
+from scapy.layers.inet6 import IPv6, ICMPv6TimeExceeded, IPv6ExtHdrFragment, \
+    ICMPv6EchoRequest
 
 
 class TestMAP(VppTestCase):
@@ -597,7 +598,7 @@ class TestMAP(VppTestCase):
         p6 = (p_ether6 / p_ip6 / payload)
         self.send_and_assert_no_replies(self.pg1, p6*1)
 
-        # Packet fragmentation
+        # UDP packet fragmentation
         payload_len = 1453
         payload = UDP(sport=40000, dport=4000) / self.payload(payload_len)
         p4 = (p_ether / p_ip4 / payload)
@@ -613,7 +614,7 @@ class TestMAP(VppTestCase):
 
         self.validate_frag_payload_len(rx, UDP, payload_len)
 
-        # Packet fragmentation send fragments
+        # UDP packet fragmentation send fragments
         payload = UDP(sport=40000, dport=4000) / self.payload(payload_len)
         p4 = (p_ether / p_ip4 / payload)
         frags = fragment_rfc791(p4, fragsize=1000)
@@ -627,10 +628,34 @@ class TestMAP(VppTestCase):
 
         self.validate_frag_payload_len(rx, UDP, payload_len)
 
-        # reass_pkt = reassemble(rx)
-        # p4_reply.ttl -= 1
-        # p4_reply.id = 256
-        # self.validate(reass_pkt, p4_reply)
+        # ICMP packet fragmentation
+        payload = ICMP(id=6529) / self.payload(payload_len)
+        p4 = (p_ether / p_ip4 / payload)
+        self.pg_enable_capture()
+        self.pg0.add_stream(p4)
+        self.pg_start()
+        rx = self.pg1.get_capture(2)
+
+        p_ip6_translated = IPv6(src='1234:5678:90ab:cdef:ac:1001:200:0',
+                                dst='2001:db8:160::c0a8:1:6')
+        for p in rx:
+            self.validate_frag(p, p_ip6_translated)
+
+        self.validate_frag_payload_len(rx, ICMPv6EchoRequest, payload_len)
+
+        # ICMP packet fragmentation send fragments
+        payload = ICMP(id=6529) / self.payload(payload_len)
+        p4 = (p_ether / p_ip4 / payload)
+        frags = fragment_rfc791(p4, fragsize=1000)
+        self.pg_enable_capture()
+        self.pg0.add_stream(frags)
+        self.pg_start()
+        rx = self.pg1.get_capture(2)
+
+        for p in rx:
+            self.validate_frag(p, p_ip6_translated)
+
+        self.validate_frag_payload_len(rx, ICMPv6EchoRequest, payload_len)
 
         # TCP MSS clamping
         self.vapi.map_param_set_tcp(1300)
index e78985b..a6d87f1 100644 (file)
@@ -226,8 +226,10 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
   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 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
     }
   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 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
   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)
     {