ip: rate-limit the sending of ICMP error messages
[vpp.git] / src / vnet / ip / icmp6.c
1 /*
2  * Copyright (c) 2015 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  * ip/icmp6.c: ip6 icmp
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vlib/vlib.h>
41 #include <vnet/ip/ip.h>
42 #include <vnet/pg/pg.h>
43 #include <vnet/ip/ip_sas.h>
44 #include <vnet/util/throttle.h>
45
46 /** ICMP throttling */
47 static throttle_t icmp_throttle;
48
49 static u8 *
50 format_ip6_icmp_type_and_code (u8 * s, va_list * args)
51 {
52   icmp6_type_t type = va_arg (*args, int);
53   u8 code = va_arg (*args, int);
54   char *t = 0;
55
56 #define _(n,f) case n: t = #f; break;
57
58   switch (type)
59     {
60       foreach_icmp6_type;
61
62     default:
63       break;
64     }
65
66 #undef _
67
68   if (!t)
69     return format (s, "unknown 0x%x", type);
70
71   s = format (s, "%s", t);
72
73   t = 0;
74   switch ((type << 8) | code)
75     {
76 #define _(a,n,f) case (ICMP6_##a << 8) | (n): t = #f; break;
77
78       foreach_icmp6_code;
79
80 #undef _
81     }
82
83   if (t)
84     s = format (s, " %s", t);
85
86   return s;
87 }
88
89 static u8 *
90 format_icmp6_header (u8 * s, va_list * args)
91 {
92   icmp46_header_t *icmp = va_arg (*args, icmp46_header_t *);
93   u32 max_header_bytes = va_arg (*args, u32);
94
95   /* Nothing to do. */
96   if (max_header_bytes < sizeof (icmp[0]))
97     return format (s, "ICMP header truncated");
98
99   s = format (s, "ICMP %U checksum 0x%x",
100               format_ip6_icmp_type_and_code, icmp->type, icmp->code,
101               clib_net_to_host_u16 (icmp->checksum));
102
103   if (max_header_bytes >=
104       sizeof (icmp6_neighbor_solicitation_or_advertisement_header_t) &&
105       (icmp->type == ICMP6_neighbor_solicitation ||
106        icmp->type == ICMP6_neighbor_advertisement))
107     {
108       icmp6_neighbor_solicitation_or_advertisement_header_t *icmp6_nd =
109         (icmp6_neighbor_solicitation_or_advertisement_header_t *) icmp;
110       s = format (s, "\n    target address %U",
111                   format_ip6_address, &icmp6_nd->target_address);
112     }
113
114   return s;
115 }
116
117 u8 *
118 format_icmp6_input_trace (u8 * s, va_list * va)
119 {
120   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
121   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
122   icmp6_input_trace_t *t = va_arg (*va, icmp6_input_trace_t *);
123
124   s = format (s, "%U",
125               format_ip6_header, t->packet_data, sizeof (t->packet_data));
126
127   return s;
128 }
129
130 static char *icmp_error_strings[] = {
131 #define _(f,s) s,
132   foreach_icmp6_error
133 #undef _
134 };
135
136 typedef enum
137 {
138   ICMP_INPUT_NEXT_PUNT,
139   ICMP_INPUT_N_NEXT,
140 } icmp_input_next_t;
141
142 typedef struct
143 {
144   uword *type_and_code_by_name;
145
146   uword *type_by_name;
147
148   /* Vector dispatch table indexed by [icmp type]. */
149   u8 input_next_index_by_type[256];
150
151   /* Max valid code indexed by icmp type. */
152   u8 max_valid_code_by_type[256];
153
154   /* hop_limit must be >= this value for this icmp type. */
155   u8 min_valid_hop_limit_by_type[256];
156
157   u8 min_valid_length_by_type[256];
158 } icmp6_main_t;
159
160 icmp6_main_t icmp6_main;
161
162 static uword
163 ip6_icmp_input (vlib_main_t * vm,
164                 vlib_node_runtime_t * node, vlib_frame_t * frame)
165 {
166   icmp6_main_t *im = &icmp6_main;
167   u32 *from, *to_next;
168   u32 n_left_from, n_left_to_next, next_index;
169
170   from = vlib_frame_vector_args (frame);
171   n_left_from = frame->n_vectors;
172   next_index = node->cached_next_index;
173
174   if (node->flags & VLIB_NODE_FLAG_TRACE)
175     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
176                                    /* stride */ 1,
177                                    sizeof (icmp6_input_trace_t));
178
179   while (n_left_from > 0)
180     {
181       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
182
183       while (n_left_from > 0 && n_left_to_next > 0)
184         {
185           vlib_buffer_t *b0;
186           ip6_header_t *ip0;
187           icmp46_header_t *icmp0;
188           icmp6_type_t type0;
189           u32 bi0, next0, error0, len0;
190
191           bi0 = to_next[0] = from[0];
192
193           from += 1;
194           n_left_from -= 1;
195           to_next += 1;
196           n_left_to_next -= 1;
197
198           b0 = vlib_get_buffer (vm, bi0);
199           ip0 = vlib_buffer_get_current (b0);
200           icmp0 = ip6_next_header (ip0);
201           type0 = icmp0->type;
202
203           error0 = ICMP6_ERROR_NONE;
204
205           next0 = im->input_next_index_by_type[type0];
206           error0 =
207             next0 == ICMP_INPUT_NEXT_PUNT ? ICMP6_ERROR_UNKNOWN_TYPE : error0;
208
209           /* Check code is valid for type. */
210           error0 =
211             icmp0->code >
212             im->max_valid_code_by_type[type0] ?
213             ICMP6_ERROR_INVALID_CODE_FOR_TYPE : error0;
214
215           /* Checksum is already validated by ip6_local node so we don't need to check that. */
216
217           /* Check that hop limit == 255 for certain types. */
218           error0 =
219             ip0->hop_limit <
220             im->min_valid_hop_limit_by_type[type0] ?
221             ICMP6_ERROR_INVALID_HOP_LIMIT_FOR_TYPE : error0;
222
223           len0 = clib_net_to_host_u16 (ip0->payload_length);
224           error0 =
225             len0 <
226             im->min_valid_length_by_type[type0] ?
227             ICMP6_ERROR_LENGTH_TOO_SMALL_FOR_TYPE : error0;
228
229           b0->error = node->errors[error0];
230
231           next0 = error0 != ICMP6_ERROR_NONE ? ICMP_INPUT_NEXT_PUNT : next0;
232
233           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
234                                            to_next, n_left_to_next,
235                                            bi0, next0);
236         }
237
238       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
239     }
240
241   return frame->n_vectors;
242 }
243
244 /* *INDENT-OFF* */
245 VLIB_REGISTER_NODE (ip6_icmp_input_node) = {
246   .function = ip6_icmp_input,
247   .name = "ip6-icmp-input",
248
249   .vector_size = sizeof (u32),
250
251   .format_trace = format_icmp6_input_trace,
252
253   .n_errors = ARRAY_LEN (icmp_error_strings),
254   .error_strings = icmp_error_strings,
255
256   .n_next_nodes = 1,
257   .next_nodes = {
258     [ICMP_INPUT_NEXT_PUNT] = "ip6-punt",
259   },
260 };
261 /* *INDENT-ON* */
262
263 typedef enum
264 {
265   IP6_ICMP_ERROR_NEXT_DROP,
266   IP6_ICMP_ERROR_NEXT_LOOKUP,
267   IP6_ICMP_ERROR_N_NEXT,
268 } ip6_icmp_error_next_t;
269
270 void
271 icmp6_error_set_vnet_buffer (vlib_buffer_t * b, u8 type, u8 code, u32 data)
272 {
273   vnet_buffer (b)->ip.icmp.type = type;
274   vnet_buffer (b)->ip.icmp.code = code;
275   vnet_buffer (b)->ip.icmp.data = data;
276 }
277
278 static u8
279 icmp6_icmp_type_to_error (u8 type)
280 {
281   switch (type)
282     {
283     case ICMP6_destination_unreachable:
284       return ICMP6_ERROR_DEST_UNREACH_SENT;
285     case ICMP6_packet_too_big:
286       return ICMP6_ERROR_PACKET_TOO_BIG_SENT;
287     case ICMP6_time_exceeded:
288       return ICMP6_ERROR_TTL_EXPIRE_SENT;
289     case ICMP6_parameter_problem:
290       return ICMP6_ERROR_PARAM_PROBLEM_SENT;
291     default:
292       return ICMP6_ERROR_DROP;
293     }
294 }
295
296 static uword
297 ip6_icmp_error (vlib_main_t * vm,
298                 vlib_node_runtime_t * node, vlib_frame_t * frame)
299 {
300   u32 *from, *to_next;
301   uword n_left_from, n_left_to_next;
302   ip6_icmp_error_next_t next_index;
303   u32 thread_index = vm->thread_index;
304
305   from = vlib_frame_vector_args (frame);
306   n_left_from = frame->n_vectors;
307   next_index = node->cached_next_index;
308
309   u64 seed = throttle_seed (&icmp_throttle, thread_index, vlib_time_now (vm));
310
311   if (node->flags & VLIB_NODE_FLAG_TRACE)
312     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
313                                    /* stride */ 1,
314                                    sizeof (icmp6_input_trace_t));
315
316   while (n_left_from > 0)
317     {
318       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
319
320       while (n_left_from > 0 && n_left_to_next > 0)
321         {
322           /*
323            * Duplicate first buffer and free the original chain.  Keep
324            * as much of the original packet as possible, within the
325            * minimum MTU. We chat "a little" here by keeping whatever
326            * is available in the first buffer.
327            */
328
329           u32 pi0 = ~0;
330           u32 org_pi0 = from[0];
331           u32 next0 = IP6_ICMP_ERROR_NEXT_LOOKUP;
332           u8 error0 = ICMP6_ERROR_NONE;
333           vlib_buffer_t *p0, *org_p0;
334           ip6_header_t *ip0, *out_ip0;
335           icmp46_header_t *icmp0;
336           u32 sw_if_index0;
337           int bogus_length;
338
339           org_p0 = vlib_get_buffer (vm, org_pi0);
340           ip0 = vlib_buffer_get_current (org_p0);
341
342           /* Rate limit based on the src,dst addresses in the original packet
343            */
344           u64 r0 = (ip6_address_hash_to_u64 (&ip0->dst_address) ^
345                     ip6_address_hash_to_u64 (&ip0->src_address));
346
347           if (throttle_check (&icmp_throttle, thread_index, r0, seed))
348             {
349               vlib_error_count (vm, node->node_index, ICMP4_ERROR_DROP, 1);
350               from += 1;
351               n_left_from -= 1;
352               continue;
353             }
354
355           p0 = vlib_buffer_copy_no_chain (vm, org_p0, &pi0);
356           if (!p0 || pi0 == ~0) /* Out of buffers */
357             continue;
358
359           /* Speculatively enqueue p0 to the current next frame */
360           to_next[0] = pi0;
361           from += 1;
362           to_next += 1;
363           n_left_from -= 1;
364           n_left_to_next -= 1;
365
366           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
367
368           vlib_buffer_copy_trace_flag (vm, p0, pi0);
369
370           /* Add IP header and ICMPv6 header including a 4 byte data field */
371           vlib_buffer_advance (p0,
372                                -(sizeof (ip6_header_t) +
373                                  sizeof (icmp46_header_t) + 4));
374
375           vnet_buffer (p0)->sw_if_index[VLIB_TX] = ~0;
376           p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
377           p0->current_length =
378             p0->current_length > 1280 ? 1280 : p0->current_length;
379
380           out_ip0 = vlib_buffer_get_current (p0);
381           icmp0 = (icmp46_header_t *) & out_ip0[1];
382
383           /* Fill ip header fields */
384           out_ip0->ip_version_traffic_class_and_flow_label =
385             clib_host_to_net_u32 (0x6 << 28);
386
387           out_ip0->payload_length =
388             clib_host_to_net_u16 (p0->current_length - sizeof (ip6_header_t));
389           out_ip0->protocol = IP_PROTOCOL_ICMP6;
390           out_ip0->hop_limit = 0xff;
391           out_ip0->dst_address = ip0->src_address;
392           /* Prefer a source address from "offending interface" */
393           if (!ip6_sas_by_sw_if_index (sw_if_index0, &out_ip0->dst_address,
394                                        &out_ip0->src_address))
395             { /* interface has no IP6 address - should not happen */
396               next0 = IP6_ICMP_ERROR_NEXT_DROP;
397               error0 = ICMP6_ERROR_DROP;
398             }
399
400           /* Fill icmp header fields */
401           icmp0->type = vnet_buffer (p0)->ip.icmp.type;
402           icmp0->code = vnet_buffer (p0)->ip.icmp.code;
403           *((u32 *) (icmp0 + 1)) =
404             clib_host_to_net_u32 (vnet_buffer (p0)->ip.icmp.data);
405           icmp0->checksum = 0;
406           icmp0->checksum =
407             ip6_tcp_udp_icmp_compute_checksum (vm, p0, out_ip0,
408                                                &bogus_length);
409
410           /* Update error status */
411           if (error0 == ICMP6_ERROR_NONE)
412             error0 = icmp6_icmp_type_to_error (icmp0->type);
413
414           vlib_error_count (vm, node->node_index, error0, 1);
415
416           /* Verify speculative enqueue, maybe switch current next frame */
417           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
418                                            to_next, n_left_to_next,
419                                            pi0, next0);
420         }
421       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
422     }
423
424   /*
425    * push the original buffers to error-drop, so that
426    * they can get the error counters handled, then freed
427    */
428   vlib_buffer_enqueue_to_single_next (vm, node,
429                                       vlib_frame_vector_args (frame),
430                                       IP6_ICMP_ERROR_NEXT_DROP,
431                                       frame->n_vectors);
432
433   return frame->n_vectors;
434 }
435
436 /* *INDENT-OFF* */
437 VLIB_REGISTER_NODE (ip6_icmp_error_node) = {
438   .function = ip6_icmp_error,
439   .name = "ip6-icmp-error",
440   .vector_size = sizeof (u32),
441
442   .n_errors = ARRAY_LEN (icmp_error_strings),
443   .error_strings = icmp_error_strings,
444
445   .n_next_nodes = IP6_ICMP_ERROR_N_NEXT,
446   .next_nodes = {
447     [IP6_ICMP_ERROR_NEXT_DROP] = "error-drop",
448     [IP6_ICMP_ERROR_NEXT_LOOKUP] = "ip6-lookup",
449   },
450
451   .format_trace = format_icmp6_input_trace,
452 };
453 /* *INDENT-ON* */
454
455
456 static uword
457 unformat_icmp_type_and_code (unformat_input_t * input, va_list * args)
458 {
459   icmp46_header_t *h = va_arg (*args, icmp46_header_t *);
460   icmp6_main_t *cm = &icmp6_main;
461   u32 i;
462
463   if (unformat_user (input, unformat_vlib_number_by_name,
464                      cm->type_and_code_by_name, &i))
465     {
466       h->type = (i >> 8) & 0xff;
467       h->code = (i >> 0) & 0xff;
468     }
469   else if (unformat_user (input, unformat_vlib_number_by_name,
470                           cm->type_by_name, &i))
471     {
472       h->type = i;
473       h->code = 0;
474     }
475   else
476     return 0;
477
478   return 1;
479 }
480
481 static void
482 icmp6_pg_edit_function (pg_main_t * pg,
483                         pg_stream_t * s,
484                         pg_edit_group_t * g, u32 * packets, u32 n_packets)
485 {
486   vlib_main_t *vm = vlib_get_main ();
487   u32 ip_offset, icmp_offset;
488   int bogus_length;
489
490   icmp_offset = g->start_byte_offset;
491   ip_offset = (g - 1)->start_byte_offset;
492
493   while (n_packets >= 1)
494     {
495       vlib_buffer_t *p0;
496       ip6_header_t *ip0;
497       icmp46_header_t *icmp0;
498
499       p0 = vlib_get_buffer (vm, packets[0]);
500       n_packets -= 1;
501       packets += 1;
502
503       ASSERT (p0->current_data == 0);
504       ip0 = (void *) (p0->data + ip_offset);
505       icmp0 = (void *) (p0->data + icmp_offset);
506
507       icmp0->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, p0, ip0,
508                                                            &bogus_length);
509       ASSERT (bogus_length == 0);
510     }
511 }
512
513 typedef struct
514 {
515   pg_edit_t type, code;
516   pg_edit_t checksum;
517 } pg_icmp46_header_t;
518
519 always_inline void
520 pg_icmp_header_init (pg_icmp46_header_t * p)
521 {
522   /* Initialize fields that are not bit fields in the IP header. */
523 #define _(f) pg_edit_init (&p->f, icmp46_header_t, f);
524   _(type);
525   _(code);
526   _(checksum);
527 #undef _
528 }
529
530 static uword
531 unformat_pg_icmp_header (unformat_input_t * input, va_list * args)
532 {
533   pg_stream_t *s = va_arg (*args, pg_stream_t *);
534   pg_icmp46_header_t *p;
535   u32 group_index;
536
537   p = pg_create_edit_group (s, sizeof (p[0]), sizeof (icmp46_header_t),
538                             &group_index);
539   pg_icmp_header_init (p);
540
541   p->checksum.type = PG_EDIT_UNSPECIFIED;
542
543   {
544     icmp46_header_t tmp;
545
546     if (!unformat (input, "ICMP %U", unformat_icmp_type_and_code, &tmp))
547       goto error;
548
549     pg_edit_set_fixed (&p->type, tmp.type);
550     pg_edit_set_fixed (&p->code, tmp.code);
551   }
552
553   /* Parse options. */
554   while (1)
555     {
556       if (unformat (input, "checksum %U",
557                     unformat_pg_edit, unformat_pg_number, &p->checksum))
558         ;
559
560       /* Can't parse input: try next protocol level. */
561       else
562         break;
563     }
564
565   if (!unformat_user (input, unformat_pg_payload, s))
566     goto error;
567
568   if (p->checksum.type == PG_EDIT_UNSPECIFIED)
569     {
570       pg_edit_group_t *g = pg_stream_get_group (s, group_index);
571       g->edit_function = icmp6_pg_edit_function;
572       g->edit_function_opaque = 0;
573     }
574
575   return 1;
576
577 error:
578   /* Free up any edits we may have added. */
579   pg_free_edit_group (s);
580   return 0;
581 }
582
583 void
584 icmp6_register_type (vlib_main_t * vm, icmp6_type_t type, u32 node_index)
585 {
586   icmp6_main_t *im = &icmp6_main;
587
588   ASSERT ((int) type < ARRAY_LEN (im->input_next_index_by_type));
589   im->input_next_index_by_type[type]
590     = vlib_node_add_next (vm, ip6_icmp_input_node.index, node_index);
591 }
592
593 static clib_error_t *
594 icmp6_init (vlib_main_t * vm)
595 {
596   ip_main_t *im = &ip_main;
597   ip_protocol_info_t *pi;
598   icmp6_main_t *cm = &icmp6_main;
599   clib_error_t *error;
600
601   error = vlib_call_init_function (vm, ip_main_init);
602
603   if (error)
604     return error;
605
606   pi = ip_get_protocol_info (im, IP_PROTOCOL_ICMP6);
607   pi->format_header = format_icmp6_header;
608   pi->unformat_pg_edit = unformat_pg_icmp_header;
609
610   cm->type_by_name = hash_create_string (0, sizeof (uword));
611 #define _(n,t) hash_set_mem (cm->type_by_name, #t, (n));
612   foreach_icmp6_type;
613 #undef _
614
615   cm->type_and_code_by_name = hash_create_string (0, sizeof (uword));
616 #define _(a,n,t) hash_set_mem (cm->type_by_name, #t, (n) | (ICMP6_##a << 8));
617   foreach_icmp6_code;
618 #undef _
619
620   clib_memset (cm->input_next_index_by_type,
621                ICMP_INPUT_NEXT_PUNT, sizeof (cm->input_next_index_by_type));
622   clib_memset (cm->max_valid_code_by_type, 0,
623                sizeof (cm->max_valid_code_by_type));
624
625 #define _(a,n,t) cm->max_valid_code_by_type[ICMP6_##a] = clib_max (cm->max_valid_code_by_type[ICMP6_##a], n);
626   foreach_icmp6_code;
627 #undef _
628
629   clib_memset (cm->min_valid_hop_limit_by_type, 0,
630                sizeof (cm->min_valid_hop_limit_by_type));
631   cm->min_valid_hop_limit_by_type[ICMP6_router_solicitation] = 255;
632   cm->min_valid_hop_limit_by_type[ICMP6_router_advertisement] = 255;
633   cm->min_valid_hop_limit_by_type[ICMP6_neighbor_solicitation] = 255;
634   cm->min_valid_hop_limit_by_type[ICMP6_neighbor_advertisement] = 255;
635   cm->min_valid_hop_limit_by_type[ICMP6_redirect] = 255;
636
637   clib_memset (cm->min_valid_length_by_type, sizeof (icmp46_header_t),
638                sizeof (cm->min_valid_length_by_type));
639   cm->min_valid_length_by_type[ICMP6_router_solicitation] =
640     sizeof (icmp6_neighbor_discovery_header_t);
641   cm->min_valid_length_by_type[ICMP6_router_advertisement] =
642     sizeof (icmp6_router_advertisement_header_t);
643   cm->min_valid_length_by_type[ICMP6_neighbor_solicitation] =
644     sizeof (icmp6_neighbor_solicitation_or_advertisement_header_t);
645   cm->min_valid_length_by_type[ICMP6_neighbor_advertisement] =
646     sizeof (icmp6_neighbor_solicitation_or_advertisement_header_t);
647   cm->min_valid_length_by_type[ICMP6_redirect] =
648     sizeof (icmp6_redirect_header_t);
649
650   vlib_thread_main_t *tm = &vlib_thread_main;
651   u32 n_vlib_mains = tm->n_vlib_mains;
652
653   throttle_init (&icmp_throttle, n_vlib_mains, 1e-3);
654
655   return (NULL);
656 }
657
658 VLIB_INIT_FUNCTION (icmp6_init);
659
660 /*
661  * fd.io coding-style-patch-verification: ON
662  *
663  * Local Variables:
664  * eval: (c-set-style "gnu")
665  * End:
666  */