nat: use SVR
[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   u32 inner_frag_id;
230   u32 inner_frag_offset;
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       vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
401       ip6 = vlib_buffer_get_current (p);
402       ip6->payload_length =
403         clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
404                               sizeof (*ip4));
405     }
406
407   //Translate outer IPv6
408   ip6->ip_version_traffic_class_and_flow_label =
409     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
410
411   ip6->hop_limit = ip4->ttl;
412   ip6->protocol = IP_PROTOCOL_ICMP6;
413
414   if ((rv = fn (p, ip4, ip6, ctx)) != 0)
415     return rv;
416
417   //Truncate when the packet exceeds the minimal IPv6 MTU
418   if (p->current_length > 1280)
419     {
420       ip6->payload_length = clib_host_to_net_u16 (1280 - sizeof (*ip6));
421       p->current_length = 1280; //Looks too simple to be correct...
422     }
423
424   //Recompute ICMP checksum
425   icmp->checksum = 0;
426   csum = ip_csum_with_carry (0, ip6->payload_length);
427   csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
428   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
429   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
430   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
431   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
432   csum =
433     ip_incremental_checksum (csum, icmp,
434                              clib_net_to_host_u16 (ip6->payload_length));
435   icmp->checksum = ~ip_csum_fold (csum);
436
437   return 0;
438 }
439
440 #endif /* __included_ip4_to_ip6_h__ */