FD.io VPP  v20.05-21-gb1500e9ff
Vector Packet Processing
ip4_to_ip6.h
Go to the documentation of this file.
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  */
29  ip6_header_t * ip6, void *ctx);
30 
31 /* *INDENT-OFF* */
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  */
52 {
53  if (ip->ip_version_and_header_length != 0x45 ||
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
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 
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 =
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 =
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 
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  {
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
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;
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__ */
ip4_address_t src_address
Definition: ip4_packet.h:170
static u8 icmp_to_icmp6_updater_pointer_table[]
Definition: ip4_to_ip6.h:32
#define PREDICT_TRUE(x)
Definition: clib.h:119
unsigned long u64
Definition: types.h:89
void os_panic(void)
Definition: unix-misc.c:175
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:113
uword ip_csum_t
Definition: ip_packet.h:244
static ip_csum_t ip_csum_with_carry(ip_csum_t sum, ip_csum_t x)
Definition: ip_packet.h:247
u16 flags_and_fragment_offset
Definition: ip4_packet.h:151
struct _tcp_header tcp_header_t
ip6_address_t src_address
Definition: ip6_packet.h:310
unsigned char u8
Definition: types.h:56
static int ip4_is_fragment(const ip4_header_t *i)
Definition: ip4_packet.h:213
#define u8_ptr_add(ptr, index)
Definition: ip_types.h:43
int(* ip4_to_ip6_set_fn_t)(vlib_buffer_t *b, ip4_header_t *ip4, ip6_header_t *ip6, void *ctx)
IPv4 to IPv6 set call back function type.
Definition: ip4_to_ip6.h:28
vl_api_ip6_address_t ip6
Definition: one.api:424
#define frag_id_4to6(id)
Definition: ip4_to_ip6.h:40
unsigned int u32
Definition: types.h:88
static u16 ip4_get_port(ip4_header_t *ip, u8 sender)
Get TCP/UDP port number or ICMP id from IPv4 packet.
Definition: ip4_to_ip6.h:51
long ctx[MAX_CONNS]
Definition: main.c:144
unsigned short u16
Definition: types.h:57
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:229
#define PREDICT_FALSE(x)
Definition: clib.h:118
#define always_inline
Definition: ipsec.h:28
vl_api_ip4_address_t ip4
Definition: one.api:376
static int icmp_to_icmp6_header(icmp46_header_t *icmp, ip4_header_t **inner_ip4)
Convert type and code value from ICMP4 to ICMP6.
Definition: ip4_to_ip6.h:102
#define IP4_HEADER_FLAG_MORE_FRAGMENTS
Definition: ip4_packet.h:152
static u16 ip4_get_fragment_offset(const ip4_header_t *i)
Definition: ip4_packet.h:200
#define ASSERT(truth)
ip_dscp_t tos
Definition: ip4_packet.h:141
static ip_csum_t ip_csum_sub_even(ip_csum_t c, ip_csum_t x)
Definition: ip_packet.h:272
static int icmp_to_icmp6(vlib_buffer_t *p, ip4_to_ip6_set_fn_t fn, void *ctx, ip4_to_ip6_set_fn_t inner_fn, void *inner_ctx)
Translate ICMP4 packet to ICMP6.
Definition: ip4_to_ip6.h:220
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:248
u32 ip_version_traffic_class_and_flow_label
Definition: ip6_packet.h:297
u16 payload_length
Definition: ip6_packet.h:301
static int ip4_is_first_fragment(const ip4_header_t *i)
Definition: ip4_packet.h:220
vl_api_address_t ip
Definition: l2.api:501
VLIB buffer representation.
Definition: buffer.h:102
#define u16_net_add(u, val)
Definition: ip_types.h:44
#define ip6_frag_hdr_offset_and_more(offset, more)
Definition: ip6_packet.h:673
u8 ip_version_and_header_length
Definition: ip4_packet.h:138
static ip_csum_t ip_incremental_checksum(ip_csum_t sum, void *_data, uword n_bytes)
Definition: ip_packet.h:318
static u16 ip_csum_fold(ip_csum_t c)
Definition: ip_packet.h:300
static ip_csum_t ip_csum_add_even(ip_csum_t c, ip_csum_t x)
Definition: ip_packet.h:255
ip6_address_t dst_address
Definition: ip6_packet.h:310