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