ip: translate fragmented icmp to fragmented icmp6
[vpp.git] / src / vnet / ip / ip4_to_ip6.h
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief IPv4 to IPv6 translation
18  */
19 #ifndef __included_ip4_to_ip6_h__
20 #define __included_ip4_to_ip6_h__
21
22 #include <vnet/ip/ip.h>
23
24
25 /**
26  * IPv4 to IPv6 set call back function type
27  */
28 typedef int (*ip4_to_ip6_set_fn_t) (vlib_buffer_t * b, ip4_header_t * ip4,
29                                     ip6_header_t * ip6, void *ctx);
30
31 /* *INDENT-OFF* */
32 static u8 icmp_to_icmp6_updater_pointer_table[] =
33   { 0, 1, 4, 4, ~0,
34     ~0, ~0, ~0, 7, 6,
35     ~0, ~0, 8, 8, 8,
36     8, 24, 24, 24, 24
37   };
38 /* *INDENT-ON* */
39
40 #define frag_id_4to6(id) (id)
41
42 /**
43  * @brief Get TCP/UDP port number or ICMP id from IPv4 packet.
44  *
45  * @param ip4        IPv4 header.
46  * @param sender     1 get sender port, 0 get receiver port.
47  *
48  * @returns Port number on success, 0 otherwise.
49  */
50 always_inline u16
51 ip4_get_port (ip4_header_t * ip, u8 sender)
52 {
53   if (ip->ip_version_and_header_length != 0x45 ||
54       ip4_get_fragment_offset (ip))
55     return 0;
56
57   if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) ||
58                     (ip->protocol == IP_PROTOCOL_UDP)))
59     {
60       udp_header_t *udp = (void *) (ip + 1);
61       return (sender) ? udp->src_port : udp->dst_port;
62     }
63   else if (ip->protocol == IP_PROTOCOL_ICMP)
64     {
65       icmp46_header_t *icmp = (void *) (ip + 1);
66       if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
67         {
68           return *((u16 *) (icmp + 1));
69         }
70       else if (clib_net_to_host_u16 (ip->length) >= 64)
71         {
72           ip = (ip4_header_t *) (icmp + 2);
73           if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) ||
74                             (ip->protocol == IP_PROTOCOL_UDP)))
75             {
76               udp_header_t *udp = (void *) (ip + 1);
77               return (sender) ? udp->dst_port : udp->src_port;
78             }
79           else if (ip->protocol == IP_PROTOCOL_ICMP)
80             {
81               icmp46_header_t *icmp = (void *) (ip + 1);
82               if (icmp->type == ICMP4_echo_request ||
83                   icmp->type == ICMP4_echo_reply)
84                 {
85                   return *((u16 *) (icmp + 1));
86                 }
87             }
88         }
89     }
90   return 0;
91 }
92
93 /**
94  * @brief Convert type and code value from ICMP4 to ICMP6.
95  *
96  * @param icmp      ICMP header.
97  * @param inner_ip4 Inner IPv4 header if present, 0 otherwise.
98  *
99  * @returns 0 on success, non-zero value otherwise.
100  */
101 always_inline int
102 icmp_to_icmp6_header (icmp46_header_t * icmp, ip4_header_t ** inner_ip4)
103 {
104   *inner_ip4 = NULL;
105   switch (icmp->type)
106     {
107     case ICMP4_echo_reply:
108       icmp->type = ICMP6_echo_reply;
109       break;
110     case ICMP4_echo_request:
111       icmp->type = ICMP6_echo_request;
112       break;
113     case ICMP4_destination_unreachable:
114       *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
115
116       switch (icmp->code)
117         {
118         case ICMP4_destination_unreachable_destination_unreachable_net: //0
119         case ICMP4_destination_unreachable_destination_unreachable_host:        //1
120           icmp->type = ICMP6_destination_unreachable;
121           icmp->code = ICMP6_destination_unreachable_no_route_to_destination;
122           break;
123         case ICMP4_destination_unreachable_protocol_unreachable:        //2
124           icmp->type = ICMP6_parameter_problem;
125           icmp->code = ICMP6_parameter_problem_unrecognized_next_header;
126           break;
127         case ICMP4_destination_unreachable_port_unreachable:    //3
128           icmp->type = ICMP6_destination_unreachable;
129           icmp->code = ICMP6_destination_unreachable_port_unreachable;
130           break;
131         case ICMP4_destination_unreachable_fragmentation_needed_and_dont_fragment_set:  //4
132           icmp->type =
133             ICMP6_packet_too_big;
134           icmp->code = 0;
135           {
136             u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
137             if (advertised_mtu)
138               advertised_mtu += 20;
139             else
140               advertised_mtu = 1000;    //FIXME ! (RFC 1191 - plateau value)
141
142             //FIXME: = minimum(advertised MTU+20, MTU_of_IPv6_nexthop, (MTU_of_IPv4_nexthop)+20)
143             *((u32 *) (icmp + 1)) = clib_host_to_net_u32 (advertised_mtu);
144           }
145           break;
146
147         case ICMP4_destination_unreachable_source_route_failed: //5
148         case ICMP4_destination_unreachable_destination_network_unknown: //6
149         case ICMP4_destination_unreachable_destination_host_unknown:    //7
150         case ICMP4_destination_unreachable_source_host_isolated:        //8
151         case ICMP4_destination_unreachable_network_unreachable_for_type_of_service:     //11
152         case ICMP4_destination_unreachable_host_unreachable_for_type_of_service:        //12
153           icmp->type =
154             ICMP6_destination_unreachable;
155           icmp->code = ICMP6_destination_unreachable_no_route_to_destination;
156           break;
157         case ICMP4_destination_unreachable_network_administratively_prohibited: //9
158         case ICMP4_destination_unreachable_host_administratively_prohibited:    //10
159         case ICMP4_destination_unreachable_communication_administratively_prohibited:   //13
160         case ICMP4_destination_unreachable_precedence_cutoff_in_effect: //15
161           icmp->type = ICMP6_destination_unreachable;
162           icmp->code =
163             ICMP6_destination_unreachable_destination_administratively_prohibited;
164           break;
165         case ICMP4_destination_unreachable_host_precedence_violation:   //14
166         default:
167           return -1;
168         }
169       break;
170
171     case ICMP4_time_exceeded:   //11
172       *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
173       icmp->type = ICMP6_time_exceeded;
174       break;
175
176     case ICMP4_parameter_problem:
177       *inner_ip4 = (ip4_header_t *) (((u8 *) icmp) + 8);
178
179       switch (icmp->code)
180         {
181         case ICMP4_parameter_problem_pointer_indicates_error:
182         case ICMP4_parameter_problem_bad_length:
183           icmp->type = ICMP6_parameter_problem;
184           icmp->code = ICMP6_parameter_problem_erroneous_header_field;
185           {
186             u8 ptr =
187               icmp_to_icmp6_updater_pointer_table[*((u8 *) (icmp + 1))];
188             if (ptr == 0xff)
189               return -1;
190
191             *((u32 *) (icmp + 1)) = clib_host_to_net_u32 (ptr);
192           }
193           break;
194         default:
195           //All other codes cause error
196           return -1;
197         }
198       break;
199
200     default:
201       //All other types cause error
202       return -1;
203       break;
204     }
205   return 0;
206 }
207
208 /**
209  * @brief Translate ICMP4 packet to ICMP6.
210  *
211  * @param p         Buffer to translate.
212  * @param fn        The function to translate outer header.
213  * @param ctx       A context passed in the outer header translate function.
214  * @param inner_fn  The function to translate inner header.
215  * @param inner_ctx A context passed in the inner header translate function.
216  *
217  * @returns 0 on success, non-zero value otherwise.
218  */
219 always_inline int
220 icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
221                ip4_to_ip6_set_fn_t inner_fn, void *inner_ctx)
222 {
223   ip4_header_t *ip4, *inner_ip4;
224   ip6_header_t *ip6, *inner_ip6;
225   u32 ip_len;
226   icmp46_header_t *icmp;
227   ip_csum_t csum;
228   ip6_frag_hdr_t *inner_frag;
229   ip6_frag_hdr_t *outer_frag= NULL;
230   u32 inner_frag_id;
231   u32 inner_frag_offset;
232   u32 outer_frag_id;
233   u8 inner_frag_more;
234   u16 *inner_L4_checksum = 0;
235   int rv;
236
237   ip4 = vlib_buffer_get_current (p);
238   ip_len = clib_net_to_host_u16 (ip4->length);
239   ASSERT (ip_len <= p->current_length);
240
241   icmp = (icmp46_header_t *) (ip4 + 1);
242   if (icmp_to_icmp6_header (icmp, &inner_ip4))
243     return -1;
244
245   if (inner_ip4)
246     {
247       //We have 2 headers to translate.
248       //We need to make some room in the middle of the packet
249       if (PREDICT_FALSE (ip4_is_fragment (inner_ip4)))
250         {
251           //Here it starts getting really tricky
252           //We will add a fragmentation header in the inner packet
253
254           if (!ip4_is_first_fragment (inner_ip4))
255             {
256               //For now we do not handle unless it is the first fragment
257               //Ideally we should handle the case as we are in slow path already
258               return -1;
259             }
260
261           vlib_buffer_advance (p,
262                                -2 * (sizeof (*ip6) - sizeof (*ip4)) -
263                                sizeof (*inner_frag));
264           ip6 = vlib_buffer_get_current (p);
265           memmove (u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)), ip4,
266                    20 + 8);
267           ip4 =
268             (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
269           icmp = (icmp46_header_t *) (ip4 + 1);
270
271           inner_ip6 =
272             (ip6_header_t *) u8_ptr_add (inner_ip4,
273                                          sizeof (*ip4) - sizeof (*ip6) -
274                                          sizeof (*inner_frag));
275           inner_frag =
276             (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, sizeof (*inner_ip6));
277           ip6->payload_length =
278             u16_net_add (ip4->length,
279                          sizeof (*ip6) - 2 * sizeof (*ip4) +
280                          sizeof (*inner_frag));
281           inner_frag_id = frag_id_4to6 (inner_ip4->fragment_id);
282           inner_frag_offset = ip4_get_fragment_offset (inner_ip4);
283           inner_frag_more =
284             ! !(inner_ip4->flags_and_fragment_offset &
285                 clib_net_to_host_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS));
286         }
287       else
288         {
289           vlib_buffer_advance (p, -2 * (sizeof (*ip6) - sizeof (*ip4)));
290           ip6 = vlib_buffer_get_current (p);
291           memmove (u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)), ip4,
292                    20 + 8);
293           ip4 =
294             (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
295           icmp = (icmp46_header_t *) u8_ptr_add (ip4, sizeof (*ip4));
296           inner_ip6 =
297             (ip6_header_t *) u8_ptr_add (inner_ip4,
298                                          sizeof (*ip4) - sizeof (*ip6));
299           ip6->payload_length =
300             u16_net_add (ip4->length, sizeof (*ip6) - 2 * sizeof (*ip4));
301           inner_frag = NULL;
302         }
303
304       if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_TCP))
305         {
306           inner_L4_checksum = &((tcp_header_t *) (inner_ip4 + 1))->checksum;
307           *inner_L4_checksum =
308             ip_csum_fold (ip_csum_sub_even
309                           (*inner_L4_checksum,
310                            *((u64 *) (&inner_ip4->src_address))));
311         }
312       else if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_UDP))
313         {
314           inner_L4_checksum = &((udp_header_t *) (inner_ip4 + 1))->checksum;
315           if (*inner_L4_checksum)
316             *inner_L4_checksum =
317               ip_csum_fold (ip_csum_sub_even
318                             (*inner_L4_checksum,
319                              *((u64 *) (&inner_ip4->src_address))));
320         }
321       else if (inner_ip4->protocol == IP_PROTOCOL_ICMP)
322         {
323           //We have an ICMP inside an ICMP
324           //It needs to be translated, but not for error ICMP messages
325           icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1);
326           //Only types ICMP4_echo_request and ICMP4_echo_reply are handled by icmp_to_icmp6_header
327           inner_icmp->type = (inner_icmp->type == ICMP4_echo_request) ?
328             ICMP6_echo_request : ICMP6_echo_reply;
329           inner_L4_checksum = &inner_icmp->checksum;
330           inner_ip4->protocol = IP_PROTOCOL_ICMP6;
331         }
332       else
333         {
334           /* To shut up Coverity */
335           os_panic ();
336         }
337
338       inner_ip6->ip_version_traffic_class_and_flow_label =
339         clib_host_to_net_u32 ((6 << 28) + (inner_ip4->tos << 20));
340       inner_ip6->payload_length =
341         u16_net_add (inner_ip4->length, -sizeof (*inner_ip4));
342       inner_ip6->hop_limit = inner_ip4->ttl;
343       inner_ip6->protocol = inner_ip4->protocol;
344
345       if ((rv = inner_fn (p, inner_ip4, inner_ip6, inner_ctx)) != 0)
346         return rv;
347
348       if (PREDICT_FALSE (inner_frag != NULL))
349         {
350           inner_frag->next_hdr = inner_ip6->protocol;
351           inner_frag->identification = inner_frag_id;
352           inner_frag->rsv = 0;
353           inner_frag->fragment_offset_and_more =
354             ip6_frag_hdr_offset_and_more (inner_frag_offset, inner_frag_more);
355           inner_ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
356           inner_ip6->payload_length =
357             clib_host_to_net_u16 (clib_net_to_host_u16
358                                   (inner_ip6->payload_length) +
359                                   sizeof (*inner_frag));
360         }
361
362       csum = *inner_L4_checksum;
363       if (inner_ip6->protocol == IP_PROTOCOL_ICMP6)
364         {
365           //Recompute ICMP checksum
366           icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1);
367
368           inner_icmp->checksum = 0;
369           csum = ip_csum_with_carry (0, inner_ip6->payload_length);
370           csum =
371             ip_csum_with_carry (csum,
372                                 clib_host_to_net_u16 (inner_ip6->protocol));
373           csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[0]);
374           csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[1]);
375           csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[0]);
376           csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[1]);
377           csum =
378             ip_incremental_checksum (csum, inner_icmp,
379                                      clib_net_to_host_u16
380                                      (inner_ip6->payload_length));
381           inner_icmp->checksum = ~ip_csum_fold (csum);
382         }
383       else
384         {
385           /* UDP checksum is optional */
386           if (csum)
387             {
388               csum =
389                 ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
390               csum =
391                 ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
392               csum =
393                 ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
394               csum =
395                 ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
396               *inner_L4_checksum = ip_csum_fold (csum);
397             }
398         }
399     }
400   else
401     {
402       if (PREDICT_FALSE (ip4->flags_and_fragment_offset &
403                          clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
404         {
405           vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) -
406                                sizeof (*outer_frag));
407           ip6 = vlib_buffer_get_current (p);
408           outer_frag = (ip6_frag_hdr_t *) (ip6 + 1);
409           outer_frag_id = frag_id_4to6 (ip4->fragment_id);
410         }
411       else
412         {
413           vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
414           ip6 = vlib_buffer_get_current (p);
415         }
416       ip6->payload_length =
417         clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
418                               sizeof (*ip4));
419     }
420
421   //Translate outer IPv6
422   ip6->ip_version_traffic_class_and_flow_label =
423     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
424
425   ip6->hop_limit = ip4->ttl;
426   ip6->protocol = IP_PROTOCOL_ICMP6;
427
428   if ((rv = fn (p, ip4, ip6, ctx)) != 0)
429     return rv;
430
431   if (PREDICT_FALSE (outer_frag != NULL))
432     {
433       outer_frag->next_hdr = ip6->protocol;
434       outer_frag->identification = outer_frag_id;
435       outer_frag->rsv = 0;
436       outer_frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1);
437       ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
438       ip6->payload_length = u16_net_add (ip6->payload_length,
439                                          sizeof (*outer_frag));
440     }
441
442   //Truncate when ICMPv6 error message exceeds the minimal IPv6 MTU
443   if (p->current_length > 1280 && icmp->type < 128)
444     {
445       ip6->payload_length = clib_host_to_net_u16 (1280 - sizeof (*ip6));
446       p->current_length = 1280; //Looks too simple to be correct...
447     }
448
449   //Recompute ICMP checksum
450   icmp->checksum = 0;
451   csum = ip_csum_with_carry (0, ip6->payload_length);
452   csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
453   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
454   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
455   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
456   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
457   csum =
458     ip_incremental_checksum (csum, icmp,
459                              clib_net_to_host_u16 (ip6->payload_length));
460   icmp->checksum = ~ip_csum_fold (csum);
461
462   return 0;
463 }
464
465 #endif /* __included_ip4_to_ip6_h__ */