blob: 17a11e6df0a103ee6ae757f9bb8e9f0f54b850d9 [file] [log] [blame]
Matus Fabiana774b532017-05-02 03:15:22 -07001/*
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 */
Klement Sekeraf126e742019-10-10 09:46:06 +000027typedef int (*ip6_to_ip4_icmp_set_fn_t) (ip6_header_t * ip6,
28 ip4_header_t * ip4, void *ctx);
29
30typedef 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);
Matus Fabiana774b532017-05-02 03:15:22 -070033
34/* *INDENT-OFF* */
35static 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 *
Klement Sekeraf126e742019-10-10 09:46:06 +000054 * @param vm vlib main
55 * @param b vlib buffer
Matus Fabiana774b532017-05-02 03:15:22 -070056 * @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 */
64static_always_inline int
Klement Sekeraf126e742019-10-10 09:46:06 +000065ip6_parse (vlib_main_t * vm, vlib_buffer_t * b, const ip6_header_t * ip6,
66 u32 buff_len, u8 * l4_protocol, u16 * l4_offset,
67 u16 * frag_hdr_offset)
Matus Fabiana774b532017-05-02 03:15:22 -070068{
Klement Sekeraf126e742019-10-10 09:46:06 +000069 ip6_ext_header_t *last_hdr, *frag_hdr;
70 u32 length;
71 if (ip6_walk_ext_hdr
72 (vm, b, ip6, IP_PROTOCOL_IPV6_FRAGMENTATION, &length, &frag_hdr,
73 &last_hdr))
Matus Fabiana774b532017-05-02 03:15:22 -070074 {
Klement Sekeraf126e742019-10-10 09:46:06 +000075 return -1;
76 }
77
78 if (length > 0)
79 {
80 if (frag_hdr)
81 {
82 *frag_hdr_offset = (u8 *) frag_hdr - (u8 *) ip6;
83 }
84 else
85 {
86 *frag_hdr_offset = 0;
87 }
88 *l4_protocol = last_hdr->next_hdr;
Matus Fabiana774b532017-05-02 03:15:22 -070089 }
90 else
91 {
Matus Fabiana774b532017-05-02 03:15:22 -070092 *frag_hdr_offset = 0;
Klement Sekeraf126e742019-10-10 09:46:06 +000093 *l4_protocol = ip6->protocol;
Matus Fabiana774b532017-05-02 03:15:22 -070094 }
Klement Sekeraf126e742019-10-10 09:46:06 +000095 *l4_offset = sizeof (*ip6) + length;
Matus Fabiana774b532017-05-02 03:15:22 -070096
97 return (buff_len < (*l4_offset + 4)) ||
98 (clib_net_to_host_u16 (ip6->payload_length) <
99 (*l4_offset + 4 - sizeof (*ip6)));
100}
101
102/**
Klement Sekeraf126e742019-10-10 09:46:06 +0000103 * @brief Get L4 information like port number or ICMP id from IPv6 packet.
Matus Fabiana774b532017-05-02 03:15:22 -0700104 *
105 * @param ip6 IPv6 header.
Matus Fabiana774b532017-05-02 03:15:22 -0700106 * @param buffer_len Buffer length.
Klement Sekeraf126e742019-10-10 09:46:06 +0000107 * @param ip_protocol L4 protocol
108 * @param src_port L4 src port or icmp id
109 * @param dst_post L4 dst port or icmp id
110 * @param icmp_type_or_tcp_flags ICMP type or TCP flags, if applicable
111 * @param tcp_ack_number TCP ack number, if applicable
112 * @param tcp_seq_number TCP seq number, if applicable
Matus Fabiana774b532017-05-02 03:15:22 -0700113 *
Klement Sekeraf126e742019-10-10 09:46:06 +0000114 * @returns 1 on success, 0 otherwise.
Matus Fabiana774b532017-05-02 03:15:22 -0700115 */
116always_inline u16
Klement Sekeraf126e742019-10-10 09:46:06 +0000117ip6_get_port (vlib_main_t * vm, vlib_buffer_t * b, ip6_header_t * ip6,
118 u16 buffer_len, u8 * ip_protocol, u16 * src_port,
119 u16 * dst_port, u8 * icmp_type_or_tcp_flags,
120 u32 * tcp_ack_number, u32 * tcp_seq_number)
Matus Fabiana774b532017-05-02 03:15:22 -0700121{
122 u8 l4_protocol;
123 u16 l4_offset;
124 u16 frag_offset;
125 u8 *l4;
126
Klement Sekeraf126e742019-10-10 09:46:06 +0000127 if (ip6_parse
128 (vm, b, ip6, buffer_len, &l4_protocol, &l4_offset, &frag_offset))
Matus Fabiana774b532017-05-02 03:15:22 -0700129 return 0;
130
131 if (frag_offset &&
132 ip6_frag_hdr_offset (((ip6_frag_hdr_t *)
133 u8_ptr_add (ip6, frag_offset))))
134 return 0; //Can't deal with non-first fragment for now
135
Klement Sekeraf126e742019-10-10 09:46:06 +0000136 if (ip_protocol)
137 {
138 *ip_protocol = l4_protocol;
139 }
Matus Fabiana774b532017-05-02 03:15:22 -0700140 l4 = u8_ptr_add (ip6, l4_offset);
141 if (l4_protocol == IP_PROTOCOL_TCP || l4_protocol == IP_PROTOCOL_UDP)
142 {
Klement Sekeraf126e742019-10-10 09:46:06 +0000143 if (src_port)
144 *src_port = ((udp_header_t *) (l4))->src_port;
145 if (dst_port)
146 *dst_port = ((udp_header_t *) (l4))->dst_port;
147 if (icmp_type_or_tcp_flags && l4_protocol == IP_PROTOCOL_TCP)
148 *icmp_type_or_tcp_flags = ((tcp_header_t *) (l4))->flags;
149 if (tcp_ack_number && l4_protocol == IP_PROTOCOL_TCP)
150 *tcp_ack_number = ((tcp_header_t *) (l4))->ack_number;
151 if (tcp_seq_number && l4_protocol == IP_PROTOCOL_TCP)
152 *tcp_seq_number = ((tcp_header_t *) (l4))->seq_number;
Matus Fabiana774b532017-05-02 03:15:22 -0700153 }
154 else if (l4_protocol == IP_PROTOCOL_ICMP6)
155 {
156 icmp46_header_t *icmp = (icmp46_header_t *) (l4);
Klement Sekeraf126e742019-10-10 09:46:06 +0000157 if (icmp_type_or_tcp_flags)
158 *icmp_type_or_tcp_flags = ((icmp46_header_t *) (l4))->type;
Matus Fabiana774b532017-05-02 03:15:22 -0700159 if (icmp->type == ICMP6_echo_request)
160 {
Klement Sekeraf126e742019-10-10 09:46:06 +0000161 if (src_port)
162 *src_port = ((u16 *) (icmp))[2];
163 if (dst_port)
164 *dst_port = ((u16 *) (icmp))[2];
Matus Fabiana774b532017-05-02 03:15:22 -0700165 }
166 else if (icmp->type == ICMP6_echo_reply)
167 {
Klement Sekeraf126e742019-10-10 09:46:06 +0000168 if (src_port)
169 *src_port = ((u16 *) (icmp))[2];
170 if (dst_port)
171 *dst_port = ((u16 *) (icmp))[2];
Matus Fabiana774b532017-05-02 03:15:22 -0700172 }
173 }
Klement Sekeraf126e742019-10-10 09:46:06 +0000174 return 1;
Matus Fabiana774b532017-05-02 03:15:22 -0700175}
176
177/**
178 * @brief Convert type and code value from ICMP6 to ICMP4.
179 *
180 * @param icmp ICMP header.
181 * @param inner_ip6 Inner IPv6 header if present, 0 otherwise.
182 *
183 * @returns 0 on success, non-zero value otherwise.
184 */
185static_always_inline int
186icmp6_to_icmp_header (icmp46_header_t * icmp, ip6_header_t ** inner_ip6)
187{
188 *inner_ip6 = NULL;
189 switch (icmp->type)
190 {
191 case ICMP6_echo_request:
192 icmp->type = ICMP4_echo_request;
193 break;
194 case ICMP6_echo_reply:
195 icmp->type = ICMP4_echo_reply;
196 break;
197 case ICMP6_destination_unreachable:
198 *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
199
200 switch (icmp->code)
201 {
202 case ICMP6_destination_unreachable_no_route_to_destination: //0
203 case ICMP6_destination_unreachable_beyond_scope_of_source_address: //2
204 case ICMP6_destination_unreachable_address_unreachable: //3
205 icmp->type = ICMP4_destination_unreachable;
206 icmp->code =
207 ICMP4_destination_unreachable_destination_unreachable_host;
208 break;
209 case ICMP6_destination_unreachable_destination_administratively_prohibited: //1
210 icmp->type =
211 ICMP4_destination_unreachable;
212 icmp->code =
213 ICMP4_destination_unreachable_communication_administratively_prohibited;
214 break;
215 case ICMP6_destination_unreachable_port_unreachable:
216 icmp->type = ICMP4_destination_unreachable;
217 icmp->code = ICMP4_destination_unreachable_port_unreachable;
218 break;
219 default:
220 return -1;
221 }
222 break;
223 case ICMP6_packet_too_big:
224 *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
225
226 icmp->type = ICMP4_destination_unreachable;
227 icmp->code = 4;
228 {
229 u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
230 advertised_mtu -= 20;
231 //FIXME: = minimum(advertised MTU-20, MTU_of_IPv4_nexthop, (MTU_of_IPv6_nexthop)-20)
232 ((u16 *) (icmp))[3] = clib_host_to_net_u16 (advertised_mtu);
233 }
234 break;
235
236 case ICMP6_time_exceeded:
237 *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
238
239 icmp->type = ICMP4_time_exceeded;
240 break;
241
242 case ICMP6_parameter_problem:
243 *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
244
245 switch (icmp->code)
246 {
247 case ICMP6_parameter_problem_erroneous_header_field:
248 icmp->type = ICMP4_parameter_problem;
249 icmp->code = ICMP4_parameter_problem_pointer_indicates_error;
250 u32 pointer = clib_net_to_host_u32 (*((u32 *) (icmp + 1)));
251 if (pointer >= 40)
252 return -1;
253
254 ((u8 *) (icmp + 1))[0] =
255 icmp6_to_icmp_updater_pointer_table[pointer];
256 break;
257 case ICMP6_parameter_problem_unrecognized_next_header:
258 icmp->type = ICMP4_destination_unreachable;
259 icmp->code = ICMP4_destination_unreachable_port_unreachable;
260 break;
261 case ICMP6_parameter_problem_unrecognized_option:
262 default:
263 return -1;
264 }
265 break;
266 default:
267 return -1;
268 break;
269 }
270 return 0;
271}
272
273/**
274 * @brief Translate TOS value from IPv6 to IPv4.
275 *
Klement Sekeraf126e742019-10-10 09:46:06 +0000276 * @param ip_version_traffic_class_and_flow_label in network byte order
Matus Fabiana774b532017-05-02 03:15:22 -0700277 *
278 * @returns IPv4 TOS value.
279 */
280static_always_inline u8
Klement Sekeraf126e742019-10-10 09:46:06 +0000281ip6_translate_tos (u32 ip_version_traffic_class_and_flow_label)
Matus Fabiana774b532017-05-02 03:15:22 -0700282{
Klement Sekeraf126e742019-10-10 09:46:06 +0000283 return (clib_net_to_host_u32 (ip_version_traffic_class_and_flow_label)
Matus Fabiana774b532017-05-02 03:15:22 -0700284 & 0x0ff00000) >> 20;
285}
286
287/**
288 * @brief Translate ICMP6 packet to ICMP4.
289 *
290 * @param p Buffer to translate.
291 * @param fn The function to translate outer header.
292 * @param ctx A context passed in the outer header translate function.
293 * @param inner_fn The function to translate inner header.
294 * @param inner_ctx A context passed in the inner header translate function.
295 *
296 * @returns 0 on success, non-zero value otherwise.
297 */
298always_inline int
Klement Sekeraf126e742019-10-10 09:46:06 +0000299icmp6_to_icmp (vlib_main_t * vm, vlib_buffer_t * p,
300 ip6_to_ip4_icmp_set_fn_t fn, void *ctx,
301 ip6_to_ip4_icmp_set_fn_t inner_fn, void *inner_ctx)
Matus Fabiana774b532017-05-02 03:15:22 -0700302{
303 ip6_header_t *ip6, *inner_ip6;
304 ip4_header_t *ip4, *inner_ip4;
305 u32 ip6_pay_len;
306 icmp46_header_t *icmp;
307 ip_csum_t csum;
308 int rv;
Matus Fabianc8e294b2017-11-08 00:18:15 -0800309 ip6_address_t old_src, old_dst;
Matus Fabiana774b532017-05-02 03:15:22 -0700310
311 ip6 = vlib_buffer_get_current (p);
312 ip6_pay_len = clib_net_to_host_u16 (ip6->payload_length);
313 icmp = (icmp46_header_t *) (ip6 + 1);
314 ASSERT (ip6_pay_len + sizeof (*ip6) <= p->current_length);
315
316 //No extensions headers allowed here
317 if (ip6->protocol != IP_PROTOCOL_ICMP6)
318 return -1;
319
320 //There are no fragmented ICMP messages, so no extension header for now
321 if (icmp6_to_icmp_header (icmp, &inner_ip6))
322 return -1;
323
324 if (inner_ip6)
325 {
326 u16 *inner_L4_checksum, inner_l4_offset, inner_frag_offset,
327 inner_frag_id;
328 u8 *inner_l4, inner_protocol;
329
330 //We have two headers to translate
331 // FROM
332 // [ IPv6 ]<- ext ->[IC][ IPv6 ]<- ext ->[L4 header ...
333 // Handled cases:
334 // [ IPv6 ][IC][ IPv6 ][L4 header ...
335 // [ IPv6 ][IC][ IPv6 ][Fr][L4 header ...
336 // TO
337 // [ IPv4][IC][ IPv4][L4 header ...
338
Klement Sekeraf126e742019-10-10 09:46:06 +0000339 if (ip6_parse (vm, p, inner_ip6, ip6_pay_len - 8,
Matus Fabiana774b532017-05-02 03:15:22 -0700340 &inner_protocol, &inner_l4_offset, &inner_frag_offset))
341 return -1;
342
343 inner_l4 = u8_ptr_add (inner_ip6, inner_l4_offset);
344 inner_ip4 =
345 (ip4_header_t *) u8_ptr_add (inner_l4, -sizeof (*inner_ip4));
346 if (inner_frag_offset)
347 {
348 ip6_frag_hdr_t *inner_frag =
349 (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, inner_frag_offset);
350 inner_frag_id = frag_id_6to4 (inner_frag->identification);
351 }
352 else
353 {
354 inner_frag_id = 0;
355 }
356
357 //Do the translation of the inner packet
358 if (inner_protocol == IP_PROTOCOL_TCP)
359 {
360 inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 16);
361 }
362 else if (inner_protocol == IP_PROTOCOL_UDP)
363 {
364 inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 6);
365 }
366 else if (inner_protocol == IP_PROTOCOL_ICMP6)
367 {
368 icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4;
Matus Fabiana774b532017-05-02 03:15:22 -0700369 //It cannot be of a different type as ip6_icmp_to_icmp6_in_place succeeded
370 inner_icmp->type = (inner_icmp->type == ICMP6_echo_request) ?
371 ICMP4_echo_request : ICMP4_echo_reply;
Matus Fabiana774b532017-05-02 03:15:22 -0700372 inner_protocol = IP_PROTOCOL_ICMP; //Will be copied to ip6 later
373 inner_L4_checksum = &inner_icmp->checksum;
374 }
375 else
376 {
377 return -1;
378 }
379
Matus Fabianc8e294b2017-11-08 00:18:15 -0800380 old_src.as_u64[0] = inner_ip6->src_address.as_u64[0];
381 old_src.as_u64[1] = inner_ip6->src_address.as_u64[1];
382 old_dst.as_u64[0] = inner_ip6->dst_address.as_u64[0];
383 old_dst.as_u64[1] = inner_ip6->dst_address.as_u64[1];
Matus Fabiana774b532017-05-02 03:15:22 -0700384
385 if ((rv = inner_fn (inner_ip6, inner_ip4, inner_ctx)) != 0)
386 return rv;
387
388 inner_ip4->ip_version_and_header_length =
389 IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
Klement Sekeraf126e742019-10-10 09:46:06 +0000390 inner_ip4->tos =
391 ip6_translate_tos
392 (inner_ip6->ip_version_traffic_class_and_flow_label);
Matus Fabiana774b532017-05-02 03:15:22 -0700393 inner_ip4->length =
394 u16_net_add (inner_ip6->payload_length,
395 sizeof (*ip4) + sizeof (*ip6) - inner_l4_offset);
396 inner_ip4->fragment_id = inner_frag_id;
397 inner_ip4->flags_and_fragment_offset =
398 clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
399 inner_ip4->ttl = inner_ip6->hop_limit;
400 inner_ip4->protocol = inner_protocol;
401 inner_ip4->checksum = ip4_header_checksum (inner_ip4);
402
403 if (inner_ip4->protocol == IP_PROTOCOL_ICMP)
404 {
Matus Fabian029f3d22017-06-15 02:28:50 -0700405 //Recompute ICMP checksum
406 icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4;
407 inner_icmp->checksum = 0;
Matus Fabiana774b532017-05-02 03:15:22 -0700408 csum =
Matus Fabian029f3d22017-06-15 02:28:50 -0700409 ip_incremental_checksum (0, inner_icmp,
410 clib_net_to_host_u16 (inner_ip4->length)
411 - sizeof (*inner_ip4));
412 inner_icmp->checksum = ~ip_csum_fold (csum);
Matus Fabiana774b532017-05-02 03:15:22 -0700413 }
414 else
415 {
416 //Update to new pseudo-header
Matus Fabian029f3d22017-06-15 02:28:50 -0700417 csum = *inner_L4_checksum;
Matus Fabianc8e294b2017-11-08 00:18:15 -0800418 csum = ip_csum_sub_even (csum, old_src.as_u64[0]);
419 csum = ip_csum_sub_even (csum, old_src.as_u64[1]);
420 csum = ip_csum_sub_even (csum, old_dst.as_u64[0]);
421 csum = ip_csum_sub_even (csum, old_dst.as_u64[1]);
Matus Fabiana774b532017-05-02 03:15:22 -0700422 csum = ip_csum_add_even (csum, inner_ip4->src_address.as_u32);
423 csum = ip_csum_add_even (csum, inner_ip4->dst_address.as_u32);
Matus Fabian029f3d22017-06-15 02:28:50 -0700424 *inner_L4_checksum = ip_csum_fold (csum);
Matus Fabiana774b532017-05-02 03:15:22 -0700425 }
Matus Fabiana774b532017-05-02 03:15:22 -0700426
427 //Move up icmp header
428 ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -2 * sizeof (*ip4) - 8);
Dave Barach178cf492018-11-13 16:34:13 -0500429 clib_memcpy_fast (u8_ptr_add (inner_l4, -sizeof (*ip4) - 8), icmp, 8);
Matus Fabiana774b532017-05-02 03:15:22 -0700430 icmp = (icmp46_header_t *) u8_ptr_add (inner_l4, -sizeof (*ip4) - 8);
431 }
432 else
433 {
434 //Only one header to translate
435 ip4 = (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4));
436 }
437
438 vlib_buffer_advance (p, (u32) (((u8 *) ip4) - ((u8 *) ip6)));
439
440 if ((rv = fn (ip6, ip4, ctx)) != 0)
441 return rv;
442
443 ip4->ip_version_and_header_length =
444 IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
Klement Sekeraf126e742019-10-10 09:46:06 +0000445 ip4->tos = ip6_translate_tos (ip6->ip_version_traffic_class_and_flow_label);
Matus Fabiana774b532017-05-02 03:15:22 -0700446 ip4->fragment_id = 0;
447 ip4->flags_and_fragment_offset = 0;
448 ip4->ttl = ip6->hop_limit;
449 ip4->protocol = IP_PROTOCOL_ICMP;
450 //TODO fix the length depending on offset length
451 ip4->length = u16_net_add (ip6->payload_length,
452 (inner_ip6 ==
453 NULL) ? sizeof (*ip4) : (2 * sizeof (*ip4) -
454 sizeof (*ip6)));
455 ip4->checksum = ip4_header_checksum (ip4);
456
457 //Recompute ICMP checksum
458 icmp->checksum = 0;
459 csum =
460 ip_incremental_checksum (0, icmp,
461 clib_net_to_host_u16 (ip4->length) -
462 sizeof (*ip4));
463 icmp->checksum = ~ip_csum_fold (csum);
464
465 return 0;
466}
467
Matus Fabiana774b532017-05-02 03:15:22 -0700468#endif /* __included_ip6_to_ip4_h__ */
469
470/*
471 * fd.io coding-style-patch-verification: ON
472 *
473 * Local Variables:
474 * eval: (c-set-style "gnu")
475 * End:
476 */