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