ip: extension header parsing fails for fragment header
[vpp.git] / src / vnet / ip / ip6_to_ip4.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 IPv6 to IPv4 translation
18  */
19 #ifndef __included_ip6_to_ip4_h__
20 #define __included_ip6_to_ip4_h__
21
22 #include <vnet/ip/ip.h>
23
24 /**
25  * IPv6 to IPv4 set call back function type
26  */
27 typedef int (*ip6_to_ip4_icmp_set_fn_t) (ip6_header_t * ip6,
28                                          ip4_header_t * ip4, void *ctx);
29
30 typedef int (*ip6_to_ip4_tcp_udp_set_fn_t) (vlib_buffer_t * b,
31                                             ip6_header_t * ip6,
32                                             ip4_header_t * ip4, void *ctx);
33
34 /* *INDENT-OFF* */
35 static u8 icmp6_to_icmp_updater_pointer_table[] =
36   { 0, 1, ~0, ~0,
37     2, 2, 9, 8,
38     12, 12, 12, 12,
39     12, 12, 12, 12,
40     12, 12, 12, 12,
41     12, 12, 12, 12,
42     24, 24, 24, 24,
43     24, 24, 24, 24,
44     24, 24, 24, 24,
45     24, 24, 24, 24
46   };
47 /* *INDENT-ON* */
48
49 #define frag_id_6to4(id) ((id) ^ ((id) >> 16))
50
51 /**
52  * @brief Parse some useful information from IPv6 header.
53  *
54  * @param vm              vlib main
55  * @param b               vlib buffer
56  * @param ip6             IPv6 header.
57  * @param buff_len        Buffer length.
58  * @param l4_protocol     L4 protocol number.
59  * @param l4_offset       L4 header offset.
60  * @param frag_hdr_offset Fragment header offset if present, 0 otherwise.
61  *
62  * @returns 0 on success, non-zero value otherwise.
63  */
64 static_always_inline int
65 ip6_parse (vlib_main_t *vm, vlib_buffer_t *b, ip6_header_t *ip6, u32 buff_len,
66            u8 *l4_protocol, u16 *l4_offset, u16 *frag_hdr_offset)
67 {
68   ip6_ext_hdr_chain_t hdr_chain;
69   int res =
70     ip6_ext_header_walk (b, ip6, IP_PROTOCOL_IPV6_FRAGMENTATION, &hdr_chain);
71   if (res < 0)
72     {
73       return -1;
74     }
75   if (hdr_chain.eh[res].protocol == IP_PROTOCOL_IPV6_FRAGMENTATION)
76     *frag_hdr_offset = hdr_chain.eh[res].offset;
77   else
78     *frag_hdr_offset = 0;
79
80   *l4_protocol = hdr_chain.eh[hdr_chain.length - 1].protocol;
81   *l4_offset = hdr_chain.eh[hdr_chain.length - 1].offset;
82
83   return 0;
84 }
85
86 /**
87  * @brief Get L4 information like port number or ICMP id from IPv6 packet.
88  *
89  * @param ip6        IPv6 header.
90  * @param buffer_len Buffer length.
91  * @param ip_protocol L4 protocol
92  * @param src_port L4 src port or icmp id
93  * @param dst_post L4 dst port or icmp id
94  * @param icmp_type_or_tcp_flags ICMP type or TCP flags, if applicable
95  * @param tcp_ack_number TCP ack number, if applicable
96  * @param tcp_seq_number TCP seq number, if applicable
97  *
98  * @returns 1 on success, 0 otherwise.
99  */
100 always_inline u16
101 ip6_get_port (vlib_main_t * vm, vlib_buffer_t * b, ip6_header_t * ip6,
102               u16 buffer_len, u8 * ip_protocol, u16 * src_port,
103               u16 * dst_port, u8 * icmp_type_or_tcp_flags,
104               u32 * tcp_ack_number, u32 * tcp_seq_number)
105 {
106   u8 l4_protocol;
107   u16 l4_offset;
108   u16 frag_offset;
109   u8 *l4;
110
111   if (ip6_parse (vm, b, ip6, buffer_len, &l4_protocol, &l4_offset,
112                  &frag_offset))
113     {
114       return 0;
115     }
116   if (frag_offset &&
117       ip6_frag_hdr_offset (((ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_offset))))
118     return 0;                   //Can't deal with non-first fragment for now
119
120   if (ip_protocol)
121     {
122       *ip_protocol = l4_protocol;
123     }
124   l4 = u8_ptr_add (ip6, l4_offset);
125   if (l4_protocol == IP_PROTOCOL_TCP || l4_protocol == IP_PROTOCOL_UDP)
126     {
127       if (src_port)
128         *src_port = ((udp_header_t *) (l4))->src_port;
129       if (dst_port)
130         *dst_port = ((udp_header_t *) (l4))->dst_port;
131       if (icmp_type_or_tcp_flags && l4_protocol == IP_PROTOCOL_TCP)
132         *icmp_type_or_tcp_flags = ((tcp_header_t *) (l4))->flags;
133       if (tcp_ack_number && l4_protocol == IP_PROTOCOL_TCP)
134         *tcp_ack_number = ((tcp_header_t *) (l4))->ack_number;
135       if (tcp_seq_number && l4_protocol == IP_PROTOCOL_TCP)
136         *tcp_seq_number = ((tcp_header_t *) (l4))->seq_number;
137     }
138   else if (l4_protocol == IP_PROTOCOL_ICMP6)
139     {
140       icmp46_header_t *icmp = (icmp46_header_t *) (l4);
141       if (icmp_type_or_tcp_flags)
142         *icmp_type_or_tcp_flags = ((icmp46_header_t *) (l4))->type;
143       if (icmp->type == ICMP6_echo_request)
144         {
145           if (src_port)
146             *src_port = ((u16 *) (icmp))[2];
147           if (dst_port)
148             *dst_port = ((u16 *) (icmp))[2];
149         }
150       else if (icmp->type == ICMP6_echo_reply)
151         {
152           if (src_port)
153             *src_port = ((u16 *) (icmp))[2];
154           if (dst_port)
155             *dst_port = ((u16 *) (icmp))[2];
156         }
157       else if (clib_net_to_host_u16 (ip6->payload_length) >= 64)
158         {
159           u16 ip6_pay_len;
160           ip6_header_t *inner_ip6;
161           u8 inner_l4_protocol;
162           u16 inner_l4_offset;
163           u16 inner_frag_offset;
164           u8 *inner_l4;
165
166           ip6_pay_len = clib_net_to_host_u16 (ip6->payload_length);
167           inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
168
169           if (ip6_parse (vm, b, inner_ip6, ip6_pay_len - 8,
170                          &inner_l4_protocol, &inner_l4_offset,
171                          &inner_frag_offset))
172             return 0;
173
174           if (inner_frag_offset &&
175               ip6_frag_hdr_offset (((ip6_frag_hdr_t *)
176                                     u8_ptr_add (inner_ip6,
177                                                 inner_frag_offset))))
178             return 0;
179
180           inner_l4 = u8_ptr_add (inner_ip6, inner_l4_offset);
181           if (inner_l4_protocol == IP_PROTOCOL_TCP ||
182               inner_l4_protocol == IP_PROTOCOL_UDP)
183             {
184               if (src_port)
185                 *src_port = ((udp_header_t *) (inner_l4))->dst_port;
186               if (dst_port)
187                 *dst_port = ((udp_header_t *) (inner_l4))->src_port;
188             }
189           else if (inner_l4_protocol == IP_PROTOCOL_ICMP6)
190             {
191               icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_l4);
192               if (inner_icmp->type == ICMP6_echo_request)
193                 {
194                   if (src_port)
195                     *src_port = ((u16 *) (inner_icmp))[2];
196                   if (dst_port)
197                     *dst_port = ((u16 *) (inner_icmp))[2];
198                 }
199               else if (inner_icmp->type == ICMP6_echo_reply)
200                 {
201                   if (src_port)
202                     *src_port = ((u16 *) (inner_icmp))[2];
203                   if (dst_port)
204                     *dst_port = ((u16 *) (inner_icmp))[2];
205                 }
206             }
207         }
208     }
209   return 1;
210 }
211
212 /**
213  * @brief Convert type and code value from ICMP6 to ICMP4.
214  *
215  * @param icmp      ICMP header.
216  * @param inner_ip6 Inner IPv6 header if present, 0 otherwise.
217  *
218  * @returns 0 on success, non-zero value otherwise.
219  */
220 static_always_inline int
221 icmp6_to_icmp_header (icmp46_header_t * icmp, ip6_header_t ** inner_ip6)
222 {
223   *inner_ip6 = NULL;
224   switch (icmp->type)
225     {
226     case ICMP6_echo_request:
227       icmp->type = ICMP4_echo_request;
228       break;
229     case ICMP6_echo_reply:
230       icmp->type = ICMP4_echo_reply;
231       break;
232     case ICMP6_destination_unreachable:
233       *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
234
235       switch (icmp->code)
236         {
237         case ICMP6_destination_unreachable_no_route_to_destination:     //0
238         case ICMP6_destination_unreachable_beyond_scope_of_source_address:      //2
239         case ICMP6_destination_unreachable_address_unreachable: //3
240           icmp->type = ICMP4_destination_unreachable;
241           icmp->code =
242             ICMP4_destination_unreachable_destination_unreachable_host;
243           break;
244         case ICMP6_destination_unreachable_destination_administratively_prohibited:     //1
245           icmp->type =
246             ICMP4_destination_unreachable;
247           icmp->code =
248             ICMP4_destination_unreachable_communication_administratively_prohibited;
249           break;
250         case ICMP6_destination_unreachable_port_unreachable:
251           icmp->type = ICMP4_destination_unreachable;
252           icmp->code = ICMP4_destination_unreachable_port_unreachable;
253           break;
254         default:
255           return -1;
256         }
257       break;
258     case ICMP6_packet_too_big:
259       *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
260
261       icmp->type = ICMP4_destination_unreachable;
262       icmp->code = 4;
263       {
264         u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
265         advertised_mtu -= 20;
266         //FIXME: = minimum(advertised MTU-20, MTU_of_IPv4_nexthop, (MTU_of_IPv6_nexthop)-20)
267         ((u16 *) (icmp))[3] = clib_host_to_net_u16 (advertised_mtu);
268       }
269       break;
270
271     case ICMP6_time_exceeded:
272       *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
273
274       icmp->type = ICMP4_time_exceeded;
275       break;
276
277     case ICMP6_parameter_problem:
278       *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
279
280       switch (icmp->code)
281         {
282         case ICMP6_parameter_problem_erroneous_header_field:
283           icmp->type = ICMP4_parameter_problem;
284           icmp->code = ICMP4_parameter_problem_pointer_indicates_error;
285           u32 pointer = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
286           if (pointer >= 40)
287             return -1;
288
289           ((u8 *) (icmp + 1))[0] =
290             icmp6_to_icmp_updater_pointer_table[pointer];
291           break;
292         case ICMP6_parameter_problem_unrecognized_next_header:
293           icmp->type = ICMP4_destination_unreachable;
294           icmp->code = ICMP4_destination_unreachable_port_unreachable;
295           break;
296         case ICMP6_parameter_problem_unrecognized_option:
297         default:
298           return -1;
299         }
300       break;
301     default:
302       return -1;
303       break;
304     }
305   return 0;
306 }
307
308 /**
309  * @brief Translate TOS value from IPv6 to IPv4.
310  *
311  * @param ip_version_traffic_class_and_flow_label in network byte order
312  *
313  * @returns IPv4 TOS value.
314  */
315 static_always_inline u8
316 ip6_translate_tos (u32 ip_version_traffic_class_and_flow_label)
317 {
318   return (clib_net_to_host_u32 (ip_version_traffic_class_and_flow_label)
319           & 0x0ff00000) >> 20;
320 }
321
322 /**
323  * @brief Translate ICMP6 packet to ICMP4.
324  *
325  * @param p         Buffer to translate.
326  * @param fn        The function to translate outer header.
327  * @param ctx       A context passed in the outer header translate function.
328  * @param inner_fn  The function to translate inner header.
329  * @param inner_ctx A context passed in the inner header translate function.
330  *
331  * @returns 0 on success, non-zero value otherwise.
332  */
333 always_inline int
334 icmp6_to_icmp (vlib_main_t * vm, vlib_buffer_t * p,
335                ip6_to_ip4_icmp_set_fn_t fn, void *ctx,
336                ip6_to_ip4_icmp_set_fn_t inner_fn, void *inner_ctx)
337 {
338   ip6_header_t *ip6, *inner_ip6;
339   ip4_header_t *ip4, *inner_ip4;
340   u32 ip6_pay_len;
341   icmp46_header_t *icmp;
342   ip_csum_t csum;
343   int rv;
344   ip6_address_t old_src, old_dst;
345
346   ip6 = vlib_buffer_get_current (p);
347   ip6_pay_len = clib_net_to_host_u16 (ip6->payload_length);
348   icmp = (icmp46_header_t *) (ip6 + 1);
349   ASSERT (ip6_pay_len + sizeof (*ip6) <= p->current_length);
350
351   //No extensions headers allowed here
352   if (ip6->protocol != IP_PROTOCOL_ICMP6)
353     return -1;
354
355   //There are no fragmented ICMP messages, so no extension header for now
356   if (icmp6_to_icmp_header (icmp, &inner_ip6))
357     return -1;
358
359   if (inner_ip6)
360     {
361       u16 *inner_L4_checksum, inner_l4_offset, inner_frag_offset,
362         inner_frag_id;
363       u8 *inner_l4, inner_protocol;
364
365       //We have two headers to translate
366       //   FROM
367       //   [   IPv6   ]<- ext ->[IC][   IPv6   ]<- ext ->[L4 header ...
368       // Handled cases:
369       //                     [   IPv6   ][IC][   IPv6   ][L4 header ...
370       //                 [   IPv6   ][IC][   IPv6   ][Fr][L4 header ...
371       //    TO
372       //                               [ IPv4][IC][ IPv4][L4 header ...
373
374       if (ip6_parse (vm, p, inner_ip6, ip6_pay_len - 8,
375                      &inner_protocol, &inner_l4_offset, &inner_frag_offset))
376         return -1;
377
378       inner_l4 = u8_ptr_add (inner_ip6, inner_l4_offset);
379       inner_ip4 =
380         (ip4_header_t *) u8_ptr_add (inner_l4, -sizeof (*inner_ip4));
381       if (inner_frag_offset)
382         {
383           ip6_frag_hdr_t *inner_frag =
384             (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, inner_frag_offset);
385           inner_frag_id = frag_id_6to4 (inner_frag->identification);
386         }
387       else
388         {
389           inner_frag_id = 0;
390         }
391
392       //Do the translation of the inner packet
393       if (inner_protocol == IP_PROTOCOL_TCP)
394         {
395           inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 16);
396         }
397       else if (inner_protocol == IP_PROTOCOL_UDP)
398         {
399           inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 6);
400         }
401       else if (inner_protocol == IP_PROTOCOL_ICMP6)
402         {
403           icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4;
404           //It cannot be of a different type as ip6_icmp_to_icmp6_in_place succeeded
405           inner_icmp->type = (inner_icmp->type == ICMP6_echo_request) ?
406             ICMP4_echo_request : ICMP4_echo_reply;
407           inner_protocol = IP_PROTOCOL_ICMP;    //Will be copied to ip6 later
408           inner_L4_checksum = &inner_icmp->checksum;
409         }
410       else
411         {
412           return -1;
413         }
414
415       old_src.as_u64[0] = inner_ip6->src_address.as_u64[0];
416       old_src.as_u64[1] = inner_ip6->src_address.as_u64[1];
417       old_dst.as_u64[0] = inner_ip6->dst_address.as_u64[0];
418       old_dst.as_u64[1] = inner_ip6->dst_address.as_u64[1];
419
420       if ((rv = inner_fn (inner_ip6, inner_ip4, inner_ctx)) != 0)
421         return rv;
422
423       inner_ip4->ip_version_and_header_length =
424         IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
425       inner_ip4->tos =
426         ip6_translate_tos
427         (inner_ip6->ip_version_traffic_class_and_flow_label);
428       inner_ip4->length =
429         u16_net_add (inner_ip6->payload_length,
430                      sizeof (*ip4) + sizeof (*ip6) - inner_l4_offset);
431       inner_ip4->fragment_id = inner_frag_id;
432       inner_ip4->flags_and_fragment_offset =
433         clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
434       inner_ip4->ttl = inner_ip6->hop_limit;
435       inner_ip4->protocol = inner_protocol;
436       inner_ip4->checksum = ip4_header_checksum (inner_ip4);
437
438       if (inner_ip4->protocol == IP_PROTOCOL_ICMP)
439         {
440           //Recompute ICMP checksum
441           icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4;
442           inner_icmp->checksum = 0;
443           csum =
444             ip_incremental_checksum (0, inner_icmp,
445                                      clib_net_to_host_u16 (inner_ip4->length)
446                                      - sizeof (*inner_ip4));
447           inner_icmp->checksum = ~ip_csum_fold (csum);
448         }
449       else
450         {
451           //Update to new pseudo-header
452           csum = *inner_L4_checksum;
453           csum = ip_csum_sub_even (csum, old_src.as_u64[0]);
454           csum = ip_csum_sub_even (csum, old_src.as_u64[1]);
455           csum = ip_csum_sub_even (csum, old_dst.as_u64[0]);
456           csum = ip_csum_sub_even (csum, old_dst.as_u64[1]);
457           csum = ip_csum_add_even (csum, inner_ip4->src_address.as_u32);
458           csum = ip_csum_add_even (csum, inner_ip4->dst_address.as_u32);
459           *inner_L4_checksum = ip_csum_fold (csum);
460         }
461
462       //Move up icmp header
463       ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -2 * sizeof (*ip4) - 8);
464       clib_memcpy_fast (u8_ptr_add (inner_l4, -sizeof (*ip4) - 8), icmp, 8);
465       icmp = (icmp46_header_t *) u8_ptr_add (inner_l4, -sizeof (*ip4) - 8);
466     }
467   else
468     {
469       //Only one header to translate
470       ip4 = (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
471     }
472
473   vlib_buffer_advance (p, (u32) (((u8 *) ip4) - ((u8 *) ip6)));
474
475   if ((rv = fn (ip6, ip4, ctx)) != 0)
476     return rv;
477
478   ip4->ip_version_and_header_length =
479     IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
480   ip4->tos = ip6_translate_tos (ip6->ip_version_traffic_class_and_flow_label);
481   ip4->fragment_id = 0;
482   ip4->flags_and_fragment_offset = 0;
483   ip4->ttl = ip6->hop_limit;
484   ip4->protocol = IP_PROTOCOL_ICMP;
485   //TODO fix the length depending on offset length
486   ip4->length = u16_net_add (ip6->payload_length,
487                              (inner_ip6 ==
488                               NULL) ? sizeof (*ip4) : (2 * sizeof (*ip4) -
489                                                        sizeof (*ip6)));
490   ip4->checksum = ip4_header_checksum (ip4);
491
492   //Recompute ICMP checksum
493   icmp->checksum = 0;
494   csum =
495     ip_incremental_checksum (0, icmp,
496                              clib_net_to_host_u16 (ip4->length) -
497                              sizeof (*ip4));
498   icmp->checksum = ~ip_csum_fold (csum);
499
500   return 0;
501 }
502
503 #endif /* __included_ip6_to_ip4_h__ */
504
505 /*
506  * fd.io coding-style-patch-verification: ON
507  *
508  * Local Variables:
509  * eval: (c-set-style "gnu")
510  * End:
511  */