NAT64: Move IPv6-IPv4 virtual reassembly code from MAP-T to common library (VPP-708)
[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) (ip4_header_t * ip4, ip6_header_t * ip6,
29                                     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           clib_memcpy (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           clib_memcpy (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             {
315               return -1;
316             }
317           *inner_L4_checksum =
318             ip_csum_fold (ip_csum_sub_even
319                           (*inner_L4_checksum,
320                            *((u64 *) (&inner_ip4->src_address))));
321         }
322       else if (inner_ip4->protocol == IP_PROTOCOL_ICMP)
323         {
324           //We have an ICMP inside an ICMP
325           //It needs to be translated, but not for error ICMP messages
326           icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1);
327           csum = inner_icmp->checksum;
328           //Only types ICMP4_echo_request and ICMP4_echo_reply are handled by icmp_to_icmp6_header
329           csum = ip_csum_sub_even (csum, *((u16 *) inner_icmp));
330           inner_icmp->type = (inner_icmp->type == ICMP4_echo_request) ?
331             ICMP6_echo_request : ICMP6_echo_reply;
332           csum = ip_csum_add_even (csum, *((u16 *) inner_icmp));
333           csum =
334             ip_csum_add_even (csum, clib_host_to_net_u16 (IP_PROTOCOL_ICMP6));
335           csum =
336             ip_csum_add_even (csum, inner_ip4->length - sizeof (*inner_ip4));
337           inner_icmp->checksum = ip_csum_fold (csum);
338           inner_L4_checksum = &inner_icmp->checksum;
339           inner_ip4->protocol = IP_PROTOCOL_ICMP6;
340         }
341       else
342         {
343           /* To shut up Coverity */
344           os_panic ();
345         }
346
347       csum = *inner_L4_checksum;        //Initial checksum of the inner L4 header
348
349       inner_ip6->ip_version_traffic_class_and_flow_label =
350         clib_host_to_net_u32 ((6 << 28) + (inner_ip4->tos << 20));
351       inner_ip6->payload_length =
352         u16_net_add (inner_ip4->length, -sizeof (*inner_ip4));
353       inner_ip6->hop_limit = inner_ip4->ttl;
354       inner_ip6->protocol = inner_ip4->protocol;
355
356       if ((rv = inner_fn (inner_ip4, inner_ip6, inner_ctx)) != 0)
357         return rv;
358
359       if (PREDICT_FALSE (inner_frag != NULL))
360         {
361           inner_frag->next_hdr = inner_ip6->protocol;
362           inner_frag->identification = inner_frag_id;
363           inner_frag->rsv = 0;
364           inner_frag->fragment_offset_and_more =
365             ip6_frag_hdr_offset_and_more (inner_frag_offset, inner_frag_more);
366           inner_ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
367           inner_ip6->payload_length =
368             clib_host_to_net_u16 (clib_net_to_host_u16
369                                   (inner_ip6->payload_length) +
370                                   sizeof (*inner_frag));
371         }
372
373       csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
374       csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
375       csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
376       csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
377       *inner_L4_checksum = ip_csum_fold (csum);
378     }
379   else
380     {
381       vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
382       ip6 = vlib_buffer_get_current (p);
383       ip6->payload_length =
384         clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
385                               sizeof (*ip4));
386     }
387
388   //Translate outer IPv6
389   ip6->ip_version_traffic_class_and_flow_label =
390     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
391
392   ip6->hop_limit = ip4->ttl;
393   ip6->protocol = IP_PROTOCOL_ICMP6;
394
395   if ((rv = fn (ip4, ip6, ctx)) != 0)
396     return rv;
397
398   //Truncate when the packet exceeds the minimal IPv6 MTU
399   if (p->current_length > 1280)
400     {
401       ip6->payload_length = clib_host_to_net_u16 (1280 - sizeof (*ip6));
402       p->current_length = 1280; //Looks too simple to be correct...
403     }
404
405   //Recompute ICMP checksum
406   icmp->checksum = 0;
407   csum = ip_csum_with_carry (0, ip6->payload_length);
408   csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
409   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
410   csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
411   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
412   csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
413   csum =
414     ip_incremental_checksum (csum, icmp,
415                              clib_net_to_host_u16 (ip6->payload_length));
416   icmp->checksum = ~ip_csum_fold (csum);
417
418   return 0;
419 }
420
421 /**
422  * @brief Translate IPv4 fragmented packet to IPv6.
423  *
424  * @param p   Buffer to translate.
425  * @param fn  The function to translate header.
426  * @param ctx A context passed in the header translate function.
427  *
428  * @returns 0 on success, non-zero value otherwise.
429  */
430 always_inline int
431 ip4_to_ip6_fragmented (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx)
432 {
433   ip4_header_t *ip4;
434   ip6_header_t *ip6;
435   ip6_frag_hdr_t *frag;
436   int rv;
437
438   ip4 = vlib_buffer_get_current (p);
439   frag = (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag));
440   ip6 =
441     (ip6_header_t *) u8_ptr_add (ip4,
442                                  sizeof (*ip4) - sizeof (*frag) -
443                                  sizeof (*ip6));
444   vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag));
445
446   //We know that the protocol was one of ICMP, TCP or UDP
447   //because the first fragment was found and cached
448   frag->next_hdr =
449     (ip4->protocol == IP_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP6 : ip4->protocol;
450   frag->identification = frag_id_4to6 (ip4->fragment_id);
451   frag->rsv = 0;
452   frag->fragment_offset_and_more =
453     ip6_frag_hdr_offset_and_more (ip4_get_fragment_offset (ip4),
454                                   clib_net_to_host_u16
455                                   (ip4->flags_and_fragment_offset) &
456                                   IP4_HEADER_FLAG_MORE_FRAGMENTS);
457
458   ip6->ip_version_traffic_class_and_flow_label =
459     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
460   ip6->payload_length =
461     clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
462                           sizeof (*ip4) + sizeof (*frag));
463   ip6->hop_limit = ip4->ttl;
464   ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
465
466   if ((rv = fn (ip4, ip6, ctx)) != 0)
467     return rv;
468
469   return 0;
470 }
471
472 /**
473  * @brief Translate IPv4 UDP/TCP packet to IPv6.
474  *
475  * @param p   Buffer to translate.
476  * @param fn  The function to translate header.
477  * @param ctx A context passed in the header translate function.
478  *
479  * @returns 0 on success, non-zero value otherwise.
480  */
481 always_inline int
482 ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx)
483 {
484   ip4_header_t *ip4;
485   ip6_header_t *ip6;
486   ip_csum_t csum;
487   u16 *checksum;
488   ip6_frag_hdr_t *frag;
489   u32 frag_id;
490   int rv;
491
492   ip4 = vlib_buffer_get_current (p);
493
494   if (ip4->protocol == IP_PROTOCOL_UDP)
495     {
496       udp_header_t *udp = ip4_next_header (ip4);
497       checksum = &udp->checksum;
498
499       //UDP checksum is optional over IPv4 but mandatory for IPv6
500       //We do not check udp->length sanity but use our safe computed value instead
501       if (PREDICT_FALSE (!checksum))
502         {
503           u16 udp_len = clib_host_to_net_u16 (ip4->length) - sizeof (*ip4);
504           csum = ip_incremental_checksum (0, udp, udp_len);
505           csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (udp_len));
506           csum =
507             ip_csum_with_carry (csum, clib_host_to_net_u16 (IP_PROTOCOL_UDP));
508           csum = ip_csum_with_carry (csum, *((u64 *) (&ip4->src_address)));
509           *checksum = ~ip_csum_fold (csum);
510         }
511     }
512   else
513     {
514       tcp_header_t *tcp = ip4_next_header (ip4);
515       checksum = &tcp->checksum;
516     }
517
518   csum = ip_csum_sub_even (*checksum, ip4->src_address.as_u32);
519   csum = ip_csum_sub_even (csum, ip4->dst_address.as_u32);
520
521   // Deal with fragmented packets
522   if (PREDICT_FALSE (ip4->flags_and_fragment_offset &
523                      clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
524     {
525       ip6 =
526         (ip6_header_t *) u8_ptr_add (ip4,
527                                      sizeof (*ip4) - sizeof (*ip6) -
528                                      sizeof (*frag));
529       frag =
530         (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag));
531       frag_id = frag_id_4to6 (ip4->fragment_id);
532       vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag));
533     }
534   else
535     {
536       ip6 = (ip6_header_t *) (((u8 *) ip4) + sizeof (*ip4) - sizeof (*ip6));
537       vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
538       frag = NULL;
539     }
540
541   ip6->ip_version_traffic_class_and_flow_label =
542     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
543   ip6->payload_length = u16_net_add (ip4->length, -sizeof (*ip4));
544   ip6->hop_limit = ip4->ttl;
545   ip6->protocol = ip4->protocol;
546
547   if (PREDICT_FALSE (frag != NULL))
548     {
549       frag->next_hdr = ip6->protocol;
550       frag->identification = frag_id;
551       frag->rsv = 0;
552       frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1);
553       ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
554       ip6->payload_length = u16_net_add (ip6->payload_length, sizeof (*frag));
555     }
556
557   if ((rv = fn (ip4, ip6, ctx)) != 0)
558     return rv;
559
560   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]);
561   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]);
562   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]);
563   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]);
564   *checksum = ip_csum_fold (csum);
565
566   return 0;
567 }
568
569 #endif /* __included_ip4_to_ip6_h__ */
570
571 /*
572  * fd.io coding-style-patch-verification: ON
573  *
574  * Local Variables:
575  * eval: (c-set-style "gnu")
576  * End:
577  */