IP4/IP6 FIB: fix crash during interface delete
[vpp.git] / src / vnet / ip / ip6_forward.c
1 /*
2  * Copyright (c) 2016 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/ip6_forward.c: IP v6 forwarding
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 <vnet/vnet.h>
41 #include <vnet/ip/ip.h>
42 #include <vnet/ip/ip6_neighbor.h>
43 #include <vnet/ethernet/ethernet.h>     /* for ethernet_header_t */
44 #include <vnet/srp/srp.h>       /* for srp_hw_interface_class */
45 #include <vppinfra/cache.h>
46 #include <vnet/fib/fib_urpf_list.h>     /* for FIB uRPF check */
47 #include <vnet/fib/ip6_fib.h>
48 #include <vnet/mfib/ip6_mfib.h>
49 #include <vnet/dpo/load_balance_map.h>
50 #include <vnet/dpo/classify_dpo.h>
51
52 #include <vppinfra/bihash_template.c>
53
54 /* Flag used by IOAM code. Classifier sets it pop-hop-by-hop checks it */
55 #define OI_DECAP   0x80000000
56
57 /**
58  * @file
59  * @brief IPv6 Forwarding.
60  *
61  * This file contains the source code for IPv6 forwarding.
62  */
63
64 void
65 ip6_forward_next_trace (vlib_main_t * vm,
66                         vlib_node_runtime_t * node,
67                         vlib_frame_t * frame,
68                         vlib_rx_or_tx_t which_adj_index);
69
70 always_inline uword
71 ip6_lookup_inline (vlib_main_t * vm,
72                    vlib_node_runtime_t * node, vlib_frame_t * frame)
73 {
74   ip6_main_t *im = &ip6_main;
75   vlib_combined_counter_main_t *cm = &load_balance_main.lbm_to_counters;
76   u32 n_left_from, n_left_to_next, *from, *to_next;
77   ip_lookup_next_t next;
78   u32 thread_index = vlib_get_thread_index ();
79
80   from = vlib_frame_vector_args (frame);
81   n_left_from = frame->n_vectors;
82   next = node->cached_next_index;
83
84   while (n_left_from > 0)
85     {
86       vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
87
88       while (n_left_from >= 4 && n_left_to_next >= 2)
89         {
90           vlib_buffer_t *p0, *p1;
91           u32 pi0, pi1, lbi0, lbi1, wrong_next;
92           ip_lookup_next_t next0, next1;
93           ip6_header_t *ip0, *ip1;
94           ip6_address_t *dst_addr0, *dst_addr1;
95           u32 fib_index0, fib_index1;
96           u32 flow_hash_config0, flow_hash_config1;
97           const dpo_id_t *dpo0, *dpo1;
98           const load_balance_t *lb0, *lb1;
99
100           /* Prefetch next iteration. */
101           {
102             vlib_buffer_t *p2, *p3;
103
104             p2 = vlib_get_buffer (vm, from[2]);
105             p3 = vlib_get_buffer (vm, from[3]);
106
107             vlib_prefetch_buffer_header (p2, LOAD);
108             vlib_prefetch_buffer_header (p3, LOAD);
109             CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD);
110             CLIB_PREFETCH (p3->data, sizeof (ip0[0]), LOAD);
111           }
112
113           pi0 = to_next[0] = from[0];
114           pi1 = to_next[1] = from[1];
115
116           p0 = vlib_get_buffer (vm, pi0);
117           p1 = vlib_get_buffer (vm, pi1);
118
119           ip0 = vlib_buffer_get_current (p0);
120           ip1 = vlib_buffer_get_current (p1);
121
122           dst_addr0 = &ip0->dst_address;
123           dst_addr1 = &ip1->dst_address;
124
125           fib_index0 =
126             vec_elt (im->fib_index_by_sw_if_index,
127                      vnet_buffer (p0)->sw_if_index[VLIB_RX]);
128           fib_index1 =
129             vec_elt (im->fib_index_by_sw_if_index,
130                      vnet_buffer (p1)->sw_if_index[VLIB_RX]);
131
132           fib_index0 = (vnet_buffer (p0)->sw_if_index[VLIB_TX] == (u32) ~ 0) ?
133             fib_index0 : vnet_buffer (p0)->sw_if_index[VLIB_TX];
134           fib_index1 = (vnet_buffer (p1)->sw_if_index[VLIB_TX] == (u32) ~ 0) ?
135             fib_index1 : vnet_buffer (p1)->sw_if_index[VLIB_TX];
136
137           lbi0 = ip6_fib_table_fwding_lookup (im, fib_index0, dst_addr0);
138           lbi1 = ip6_fib_table_fwding_lookup (im, fib_index1, dst_addr1);
139
140           lb0 = load_balance_get (lbi0);
141           lb1 = load_balance_get (lbi1);
142           ASSERT (lb0->lb_n_buckets > 0);
143           ASSERT (lb1->lb_n_buckets > 0);
144           ASSERT (is_pow2 (lb0->lb_n_buckets));
145           ASSERT (is_pow2 (lb1->lb_n_buckets));
146
147           vnet_buffer (p0)->ip.flow_hash = vnet_buffer (p1)->ip.flow_hash = 0;
148
149           if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
150             {
151               flow_hash_config0 = lb0->lb_hash_config;
152               vnet_buffer (p0)->ip.flow_hash =
153                 ip6_compute_flow_hash (ip0, flow_hash_config0);
154               dpo0 =
155                 load_balance_get_fwd_bucket (lb0,
156                                              (vnet_buffer (p0)->ip.flow_hash &
157                                               (lb0->lb_n_buckets_minus_1)));
158             }
159           else
160             {
161               dpo0 = load_balance_get_bucket_i (lb0, 0);
162             }
163           if (PREDICT_FALSE (lb1->lb_n_buckets > 1))
164             {
165               flow_hash_config1 = lb1->lb_hash_config;
166               vnet_buffer (p1)->ip.flow_hash =
167                 ip6_compute_flow_hash (ip1, flow_hash_config1);
168               dpo1 =
169                 load_balance_get_fwd_bucket (lb1,
170                                              (vnet_buffer (p1)->ip.flow_hash &
171                                               (lb1->lb_n_buckets_minus_1)));
172             }
173           else
174             {
175               dpo1 = load_balance_get_bucket_i (lb1, 0);
176             }
177           next0 = dpo0->dpoi_next_node;
178           next1 = dpo1->dpoi_next_node;
179
180           /* Only process the HBH Option Header if explicitly configured to do so */
181           if (PREDICT_FALSE
182               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
183             {
184               next0 = (dpo_is_adj (dpo0) && im->hbh_enabled) ?
185                 (ip_lookup_next_t) IP6_LOOKUP_NEXT_HOP_BY_HOP : next0;
186             }
187           if (PREDICT_FALSE
188               (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
189             {
190               next1 = (dpo_is_adj (dpo1) && im->hbh_enabled) ?
191                 (ip_lookup_next_t) IP6_LOOKUP_NEXT_HOP_BY_HOP : next1;
192             }
193           vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
194           vnet_buffer (p1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
195
196           vlib_increment_combined_counter
197             (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
198           vlib_increment_combined_counter
199             (cm, thread_index, lbi1, 1, vlib_buffer_length_in_chain (vm, p1));
200
201           from += 2;
202           to_next += 2;
203           n_left_to_next -= 2;
204           n_left_from -= 2;
205
206           wrong_next = (next0 != next) + 2 * (next1 != next);
207           if (PREDICT_FALSE (wrong_next != 0))
208             {
209               switch (wrong_next)
210                 {
211                 case 1:
212                   /* A B A */
213                   to_next[-2] = pi1;
214                   to_next -= 1;
215                   n_left_to_next += 1;
216                   vlib_set_next_frame_buffer (vm, node, next0, pi0);
217                   break;
218
219                 case 2:
220                   /* A A B */
221                   to_next -= 1;
222                   n_left_to_next += 1;
223                   vlib_set_next_frame_buffer (vm, node, next1, pi1);
224                   break;
225
226                 case 3:
227                   /* A B C */
228                   to_next -= 2;
229                   n_left_to_next += 2;
230                   vlib_set_next_frame_buffer (vm, node, next0, pi0);
231                   vlib_set_next_frame_buffer (vm, node, next1, pi1);
232                   if (next0 == next1)
233                     {
234                       /* A B B */
235                       vlib_put_next_frame (vm, node, next, n_left_to_next);
236                       next = next1;
237                       vlib_get_next_frame (vm, node, next, to_next,
238                                            n_left_to_next);
239                     }
240                 }
241             }
242         }
243
244       while (n_left_from > 0 && n_left_to_next > 0)
245         {
246           vlib_buffer_t *p0;
247           ip6_header_t *ip0;
248           u32 pi0, lbi0;
249           ip_lookup_next_t next0;
250           load_balance_t *lb0;
251           ip6_address_t *dst_addr0;
252           u32 fib_index0, flow_hash_config0;
253           const dpo_id_t *dpo0;
254
255           pi0 = from[0];
256           to_next[0] = pi0;
257
258           p0 = vlib_get_buffer (vm, pi0);
259
260           ip0 = vlib_buffer_get_current (p0);
261
262           dst_addr0 = &ip0->dst_address;
263
264           fib_index0 =
265             vec_elt (im->fib_index_by_sw_if_index,
266                      vnet_buffer (p0)->sw_if_index[VLIB_RX]);
267           fib_index0 =
268             (vnet_buffer (p0)->sw_if_index[VLIB_TX] ==
269              (u32) ~ 0) ? fib_index0 : vnet_buffer (p0)->sw_if_index[VLIB_TX];
270
271           lbi0 = ip6_fib_table_fwding_lookup (im, fib_index0, dst_addr0);
272
273           lb0 = load_balance_get (lbi0);
274           flow_hash_config0 = lb0->lb_hash_config;
275
276           vnet_buffer (p0)->ip.flow_hash = 0;
277           ASSERT (lb0->lb_n_buckets > 0);
278           ASSERT (is_pow2 (lb0->lb_n_buckets));
279
280           if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
281             {
282               flow_hash_config0 = lb0->lb_hash_config;
283               vnet_buffer (p0)->ip.flow_hash =
284                 ip6_compute_flow_hash (ip0, flow_hash_config0);
285               dpo0 =
286                 load_balance_get_fwd_bucket (lb0,
287                                              (vnet_buffer (p0)->ip.flow_hash &
288                                               (lb0->lb_n_buckets_minus_1)));
289             }
290           else
291             {
292               dpo0 = load_balance_get_bucket_i (lb0, 0);
293             }
294
295           dpo0 = load_balance_get_bucket_i (lb0,
296                                             (vnet_buffer (p0)->ip.flow_hash &
297                                              lb0->lb_n_buckets_minus_1));
298           next0 = dpo0->dpoi_next_node;
299
300           /* Only process the HBH Option Header if explicitly configured to do so */
301           if (PREDICT_FALSE
302               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
303             {
304               next0 = (dpo_is_adj (dpo0) && im->hbh_enabled) ?
305                 (ip_lookup_next_t) IP6_LOOKUP_NEXT_HOP_BY_HOP : next0;
306             }
307           vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
308
309           vlib_increment_combined_counter
310             (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
311
312           from += 1;
313           to_next += 1;
314           n_left_to_next -= 1;
315           n_left_from -= 1;
316
317           if (PREDICT_FALSE (next0 != next))
318             {
319               n_left_to_next += 1;
320               vlib_put_next_frame (vm, node, next, n_left_to_next);
321               next = next0;
322               vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
323               to_next[0] = pi0;
324               to_next += 1;
325               n_left_to_next -= 1;
326             }
327         }
328
329       vlib_put_next_frame (vm, node, next, n_left_to_next);
330     }
331
332   if (node->flags & VLIB_NODE_FLAG_TRACE)
333     ip6_forward_next_trace (vm, node, frame, VLIB_TX);
334
335   return frame->n_vectors;
336 }
337
338 static void
339 ip6_add_interface_routes (vnet_main_t * vnm, u32 sw_if_index,
340                           ip6_main_t * im, u32 fib_index,
341                           ip_interface_address_t * a)
342 {
343   ip_lookup_main_t *lm = &im->lookup_main;
344   ip6_address_t *address = ip_interface_address_get_address (lm, a);
345   fib_prefix_t pfx = {
346     .fp_len = a->address_length,
347     .fp_proto = FIB_PROTOCOL_IP6,
348     .fp_addr.ip6 = *address,
349   };
350
351   if (a->address_length < 128)
352     {
353       fib_table_entry_update_one_path (fib_index,
354                                        &pfx,
355                                        FIB_SOURCE_INTERFACE,
356                                        (FIB_ENTRY_FLAG_CONNECTED |
357                                         FIB_ENTRY_FLAG_ATTACHED),
358                                        FIB_PROTOCOL_IP6,
359                                        /* No next-hop address */
360                                        NULL, sw_if_index,
361                                        /* invalid FIB index */
362                                        ~0, 1,
363                                        /* no label stack */
364                                        NULL, FIB_ROUTE_PATH_FLAG_NONE);
365     }
366
367   pfx.fp_len = 128;
368   if (sw_if_index < vec_len (lm->classify_table_index_by_sw_if_index))
369     {
370       u32 classify_table_index =
371         lm->classify_table_index_by_sw_if_index[sw_if_index];
372       if (classify_table_index != (u32) ~ 0)
373         {
374           dpo_id_t dpo = DPO_INVALID;
375
376           dpo_set (&dpo,
377                    DPO_CLASSIFY,
378                    DPO_PROTO_IP6,
379                    classify_dpo_create (DPO_PROTO_IP6, classify_table_index));
380
381           fib_table_entry_special_dpo_add (fib_index,
382                                            &pfx,
383                                            FIB_SOURCE_CLASSIFY,
384                                            FIB_ENTRY_FLAG_NONE, &dpo);
385           dpo_reset (&dpo);
386         }
387     }
388
389   fib_table_entry_update_one_path (fib_index, &pfx,
390                                    FIB_SOURCE_INTERFACE,
391                                    (FIB_ENTRY_FLAG_CONNECTED |
392                                     FIB_ENTRY_FLAG_LOCAL),
393                                    FIB_PROTOCOL_IP6,
394                                    &pfx.fp_addr,
395                                    sw_if_index, ~0,
396                                    1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
397 }
398
399 static void
400 ip6_del_interface_routes (ip6_main_t * im,
401                           u32 fib_index,
402                           ip6_address_t * address, u32 address_length)
403 {
404   fib_prefix_t pfx = {
405     .fp_len = address_length,
406     .fp_proto = FIB_PROTOCOL_IP6,
407     .fp_addr.ip6 = *address,
408   };
409
410   if (pfx.fp_len < 128)
411     {
412       fib_table_entry_delete (fib_index, &pfx, FIB_SOURCE_INTERFACE);
413
414     }
415
416   pfx.fp_len = 128;
417   fib_table_entry_delete (fib_index, &pfx, FIB_SOURCE_INTERFACE);
418 }
419
420 void
421 ip6_sw_interface_enable_disable (u32 sw_if_index, u32 is_enable)
422 {
423   ip6_main_t *im = &ip6_main;
424
425   vec_validate_init_empty (im->ip_enabled_by_sw_if_index, sw_if_index, 0);
426
427   /*
428    * enable/disable only on the 1<->0 transition
429    */
430   if (is_enable)
431     {
432       if (1 != ++im->ip_enabled_by_sw_if_index[sw_if_index])
433         return;
434     }
435   else
436     {
437       /* The ref count is 0 when an address is removed from an interface that has
438        * no address - this is not a ciritical error */
439       if (0 == im->ip_enabled_by_sw_if_index[sw_if_index] ||
440           0 != --im->ip_enabled_by_sw_if_index[sw_if_index])
441         return;
442     }
443
444   vnet_feature_enable_disable ("ip6-unicast", "ip6-drop", sw_if_index,
445                                !is_enable, 0, 0);
446
447   vnet_feature_enable_disable ("ip6-multicast", "ip6-drop", sw_if_index,
448                                !is_enable, 0, 0);
449 }
450
451 /* get first interface address */
452 ip6_address_t *
453 ip6_interface_first_address (ip6_main_t * im, u32 sw_if_index)
454 {
455   ip_lookup_main_t *lm = &im->lookup_main;
456   ip_interface_address_t *ia = 0;
457   ip6_address_t *result = 0;
458
459   /* *INDENT-OFF* */
460   foreach_ip_interface_address (lm, ia, sw_if_index,
461                                 1 /* honor unnumbered */,
462   ({
463     ip6_address_t * a = ip_interface_address_get_address (lm, ia);
464     result = a;
465     break;
466   }));
467   /* *INDENT-ON* */
468   return result;
469 }
470
471 clib_error_t *
472 ip6_add_del_interface_address (vlib_main_t * vm,
473                                u32 sw_if_index,
474                                ip6_address_t * address,
475                                u32 address_length, u32 is_del)
476 {
477   vnet_main_t *vnm = vnet_get_main ();
478   ip6_main_t *im = &ip6_main;
479   ip_lookup_main_t *lm = &im->lookup_main;
480   clib_error_t *error;
481   u32 if_address_index;
482   ip6_address_fib_t ip6_af, *addr_fib = 0;
483
484   vec_validate (im->fib_index_by_sw_if_index, sw_if_index);
485   vec_validate (im->mfib_index_by_sw_if_index, sw_if_index);
486
487   ip6_addr_fib_init (&ip6_af, address,
488                      vec_elt (im->fib_index_by_sw_if_index, sw_if_index));
489   vec_add1 (addr_fib, ip6_af);
490
491   {
492     uword elts_before = pool_elts (lm->if_address_pool);
493
494     error = ip_interface_address_add_del
495       (lm, sw_if_index, addr_fib, address_length, is_del, &if_address_index);
496     if (error)
497       goto done;
498
499     /* Pool did not grow: add duplicate address. */
500     if (elts_before == pool_elts (lm->if_address_pool))
501       goto done;
502   }
503
504   ip6_sw_interface_enable_disable (sw_if_index, !is_del);
505
506   if (is_del)
507     ip6_del_interface_routes (im, ip6_af.fib_index, address, address_length);
508   else
509     ip6_add_interface_routes (vnm, sw_if_index,
510                               im, ip6_af.fib_index,
511                               pool_elt_at_index (lm->if_address_pool,
512                                                  if_address_index));
513
514   {
515     ip6_add_del_interface_address_callback_t *cb;
516     vec_foreach (cb, im->add_del_interface_address_callbacks)
517       cb->function (im, cb->function_opaque, sw_if_index,
518                     address, address_length, if_address_index, is_del);
519   }
520
521 done:
522   vec_free (addr_fib);
523   return error;
524 }
525
526 clib_error_t *
527 ip6_sw_interface_admin_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
528 {
529   ip6_main_t *im = &ip6_main;
530   ip_interface_address_t *ia;
531   ip6_address_t *a;
532   u32 is_admin_up, fib_index;
533
534   /* Fill in lookup tables with default table (0). */
535   vec_validate (im->fib_index_by_sw_if_index, sw_if_index);
536
537   vec_validate_init_empty (im->
538                            lookup_main.if_address_pool_index_by_sw_if_index,
539                            sw_if_index, ~0);
540
541   is_admin_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0;
542
543   fib_index = vec_elt (im->fib_index_by_sw_if_index, sw_if_index);
544
545   /* *INDENT-OFF* */
546   foreach_ip_interface_address (&im->lookup_main, ia, sw_if_index,
547                                 0 /* honor unnumbered */,
548   ({
549     a = ip_interface_address_get_address (&im->lookup_main, ia);
550     if (is_admin_up)
551       ip6_add_interface_routes (vnm, sw_if_index,
552                                 im, fib_index,
553                                 ia);
554     else
555       ip6_del_interface_routes (im, fib_index,
556                                 a, ia->address_length);
557   }));
558   /* *INDENT-ON* */
559
560   return 0;
561 }
562
563 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip6_sw_interface_admin_up_down);
564
565 /* Built-in ip6 unicast rx feature path definition */
566 /* *INDENT-OFF* */
567 VNET_FEATURE_ARC_INIT (ip6_unicast, static) =
568 {
569   .arc_name  = "ip6-unicast",
570   .start_nodes = VNET_FEATURES ("ip6-input"),
571   .arc_index_ptr = &ip6_main.lookup_main.ucast_feature_arc_index,
572 };
573
574 VNET_FEATURE_INIT (ip6_flow_classify, static) =
575 {
576   .arc_name = "ip6-unicast",
577   .node_name = "ip6-flow-classify",
578   .runs_before = VNET_FEATURES ("ip6-inacl"),
579 };
580
581 VNET_FEATURE_INIT (ip6_inacl, static) =
582 {
583   .arc_name = "ip6-unicast",
584   .node_name = "ip6-inacl",
585   .runs_before = VNET_FEATURES ("ip6-policer-classify"),
586 };
587
588 VNET_FEATURE_INIT (ip6_policer_classify, static) =
589 {
590   .arc_name = "ip6-unicast",
591   .node_name = "ip6-policer-classify",
592   .runs_before = VNET_FEATURES ("ipsec-input-ip6"),
593 };
594
595 VNET_FEATURE_INIT (ip6_ipsec, static) =
596 {
597   .arc_name = "ip6-unicast",
598   .node_name = "ipsec-input-ip6",
599   .runs_before = VNET_FEATURES ("l2tp-decap"),
600 };
601
602 VNET_FEATURE_INIT (ip6_l2tp, static) =
603 {
604   .arc_name = "ip6-unicast",
605   .node_name = "l2tp-decap",
606   .runs_before = VNET_FEATURES ("vpath-input-ip6"),
607 };
608
609 VNET_FEATURE_INIT (ip6_vpath, static) =
610 {
611   .arc_name = "ip6-unicast",
612   .node_name = "vpath-input-ip6",
613   .runs_before = VNET_FEATURES ("ip6-vxlan-bypass"),
614 };
615
616 VNET_FEATURE_INIT (ip6_vxlan_bypass, static) =
617 {
618   .arc_name = "ip6-unicast",
619   .node_name = "ip6-vxlan-bypass",
620   .runs_before = VNET_FEATURES ("ip6-lookup"),
621 };
622
623 VNET_FEATURE_INIT (ip6_drop, static) =
624 {
625   .arc_name = "ip6-unicast",
626   .node_name = "ip6-drop",
627   .runs_before = VNET_FEATURES ("ip6-lookup"),
628 };
629
630 VNET_FEATURE_INIT (ip6_lookup, static) =
631 {
632   .arc_name = "ip6-unicast",
633   .node_name = "ip6-lookup",
634   .runs_before = 0,  /*last feature*/
635 };
636
637 /* Built-in ip6 multicast rx feature path definition (none now) */
638 VNET_FEATURE_ARC_INIT (ip6_multicast, static) =
639 {
640   .arc_name  = "ip6-multicast",
641   .start_nodes = VNET_FEATURES ("ip6-input"),
642   .arc_index_ptr = &ip6_main.lookup_main.mcast_feature_arc_index,
643 };
644
645 VNET_FEATURE_INIT (ip6_vpath_mc, static) = {
646   .arc_name = "ip6-multicast",
647   .node_name = "vpath-input-ip6",
648   .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
649 };
650
651 VNET_FEATURE_INIT (ip6_drop_mc, static) = {
652   .arc_name = "ip6-multicast",
653   .node_name = "ip6-drop",
654   .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
655 };
656
657 VNET_FEATURE_INIT (ip6_mc_lookup, static) = {
658   .arc_name = "ip6-multicast",
659   .node_name = "ip6-mfib-forward-lookup",
660   .runs_before = 0, /* last feature */
661 };
662
663 /* Built-in ip4 tx feature path definition */
664 VNET_FEATURE_ARC_INIT (ip6_output, static) =
665 {
666   .arc_name  = "ip6-output",
667   .start_nodes = VNET_FEATURES ("ip6-rewrite", "ip6-midchain"),
668   .arc_index_ptr = &ip6_main.lookup_main.output_feature_arc_index,
669 };
670
671 VNET_FEATURE_INIT (ip6_ipsec_output, static) = {
672   .arc_name = "ip6-output",
673   .node_name = "ipsec-output-ip6",
674   .runs_before = VNET_FEATURES ("interface-output"),
675 };
676
677 VNET_FEATURE_INIT (ip6_interface_output, static) = {
678   .arc_name = "ip6-output",
679   .node_name = "interface-output",
680   .runs_before = 0, /* not before any other features */
681 };
682 /* *INDENT-ON* */
683
684 clib_error_t *
685 ip6_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
686 {
687   ip6_main_t *im = &ip6_main;
688
689   vec_validate (im->fib_index_by_sw_if_index, sw_if_index);
690   vec_validate (im->mfib_index_by_sw_if_index, sw_if_index);
691
692   if (!is_add)
693     {
694       /* Ensure that IPv6 is disabled */
695       ip6_main_t *im6 = &ip6_main;
696       ip_lookup_main_t *lm6 = &im6->lookup_main;
697       ip_interface_address_t *ia = 0;
698       ip6_address_t *address;
699       vlib_main_t *vm = vlib_get_main ();
700
701       ip6_neighbor_sw_interface_add_del (vnm, sw_if_index, 0 /* is_add */ );
702       /* *INDENT-OFF* */
703       foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* honor unnumbered */,
704       ({
705         address = ip_interface_address_get_address (lm6, ia);
706         ip6_add_del_interface_address(vm, sw_if_index, address, ia->address_length, 1);
707       }));
708       /* *INDENT-ON* */
709       ip6_mfib_interface_enable_disable (sw_if_index, 0);
710     }
711
712   vnet_feature_enable_disable ("ip6-unicast", "ip6-drop", sw_if_index,
713                                is_add, 0, 0);
714
715   vnet_feature_enable_disable ("ip6-multicast", "ip6-drop", sw_if_index,
716                                is_add, 0, 0);
717
718   return /* no error */ 0;
719 }
720
721 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip6_sw_interface_add_del);
722
723 static uword
724 ip6_lookup (vlib_main_t * vm,
725             vlib_node_runtime_t * node, vlib_frame_t * frame)
726 {
727   return ip6_lookup_inline (vm, node, frame);
728 }
729
730 static u8 *format_ip6_lookup_trace (u8 * s, va_list * args);
731
732 /* *INDENT-OFF* */
733 VLIB_REGISTER_NODE (ip6_lookup_node) =
734 {
735   .function = ip6_lookup,
736   .name = "ip6-lookup",
737   .vector_size = sizeof (u32),
738   .format_trace = format_ip6_lookup_trace,
739   .n_next_nodes = IP6_LOOKUP_N_NEXT,
740   .next_nodes = IP6_LOOKUP_NEXT_NODES,
741 };
742 /* *INDENT-ON* */
743
744 VLIB_NODE_FUNCTION_MULTIARCH (ip6_lookup_node, ip6_lookup);
745
746 always_inline uword
747 ip6_load_balance (vlib_main_t * vm,
748                   vlib_node_runtime_t * node, vlib_frame_t * frame)
749 {
750   vlib_combined_counter_main_t *cm = &load_balance_main.lbm_via_counters;
751   u32 n_left_from, n_left_to_next, *from, *to_next;
752   ip_lookup_next_t next;
753   u32 thread_index = vlib_get_thread_index ();
754   ip6_main_t *im = &ip6_main;
755
756   from = vlib_frame_vector_args (frame);
757   n_left_from = frame->n_vectors;
758   next = node->cached_next_index;
759
760   if (node->flags & VLIB_NODE_FLAG_TRACE)
761     ip6_forward_next_trace (vm, node, frame, VLIB_TX);
762
763   while (n_left_from > 0)
764     {
765       vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
766
767
768       while (n_left_from >= 4 && n_left_to_next >= 2)
769         {
770           ip_lookup_next_t next0, next1;
771           const load_balance_t *lb0, *lb1;
772           vlib_buffer_t *p0, *p1;
773           u32 pi0, lbi0, hc0, pi1, lbi1, hc1;
774           const ip6_header_t *ip0, *ip1;
775           const dpo_id_t *dpo0, *dpo1;
776
777           /* Prefetch next iteration. */
778           {
779             vlib_buffer_t *p2, *p3;
780
781             p2 = vlib_get_buffer (vm, from[2]);
782             p3 = vlib_get_buffer (vm, from[3]);
783
784             vlib_prefetch_buffer_header (p2, STORE);
785             vlib_prefetch_buffer_header (p3, STORE);
786
787             CLIB_PREFETCH (p2->data, sizeof (ip0[0]), STORE);
788             CLIB_PREFETCH (p3->data, sizeof (ip0[0]), STORE);
789           }
790
791           pi0 = to_next[0] = from[0];
792           pi1 = to_next[1] = from[1];
793
794           from += 2;
795           n_left_from -= 2;
796           to_next += 2;
797           n_left_to_next -= 2;
798
799           p0 = vlib_get_buffer (vm, pi0);
800           p1 = vlib_get_buffer (vm, pi1);
801
802           ip0 = vlib_buffer_get_current (p0);
803           ip1 = vlib_buffer_get_current (p1);
804           lbi0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
805           lbi1 = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
806
807           lb0 = load_balance_get (lbi0);
808           lb1 = load_balance_get (lbi1);
809
810           /*
811            * this node is for via FIBs we can re-use the hash value from the
812            * to node if present.
813            * We don't want to use the same hash value at each level in the recursion
814            * graph as that would lead to polarisation
815            */
816           hc0 = hc1 = 0;
817
818           if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
819             {
820               if (PREDICT_TRUE (vnet_buffer (p0)->ip.flow_hash))
821                 {
822                   hc0 = vnet_buffer (p0)->ip.flow_hash =
823                     vnet_buffer (p0)->ip.flow_hash >> 1;
824                 }
825               else
826                 {
827                   hc0 = vnet_buffer (p0)->ip.flow_hash =
828                     ip6_compute_flow_hash (ip0, lb0->lb_hash_config);
829                 }
830               dpo0 =
831                 load_balance_get_fwd_bucket (lb0,
832                                              (hc0 &
833                                               lb0->lb_n_buckets_minus_1));
834             }
835           else
836             {
837               dpo0 = load_balance_get_bucket_i (lb0, 0);
838             }
839           if (PREDICT_FALSE (lb1->lb_n_buckets > 1))
840             {
841               if (PREDICT_TRUE (vnet_buffer (p1)->ip.flow_hash))
842                 {
843                   hc1 = vnet_buffer (p1)->ip.flow_hash =
844                     vnet_buffer (p1)->ip.flow_hash >> 1;
845                 }
846               else
847                 {
848                   hc1 = vnet_buffer (p1)->ip.flow_hash =
849                     ip6_compute_flow_hash (ip1, lb1->lb_hash_config);
850                 }
851               dpo1 =
852                 load_balance_get_fwd_bucket (lb1,
853                                              (hc1 &
854                                               lb1->lb_n_buckets_minus_1));
855             }
856           else
857             {
858               dpo1 = load_balance_get_bucket_i (lb1, 0);
859             }
860
861           next0 = dpo0->dpoi_next_node;
862           next1 = dpo1->dpoi_next_node;
863
864           /* Only process the HBH Option Header if explicitly configured to do so */
865           if (PREDICT_FALSE
866               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
867             {
868               next0 = (dpo_is_adj (dpo0) && im->hbh_enabled) ?
869                 (ip_lookup_next_t) IP6_LOOKUP_NEXT_HOP_BY_HOP : next0;
870             }
871           /* Only process the HBH Option Header if explicitly configured to do so */
872           if (PREDICT_FALSE
873               (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
874             {
875               next1 = (dpo_is_adj (dpo1) && im->hbh_enabled) ?
876                 (ip_lookup_next_t) IP6_LOOKUP_NEXT_HOP_BY_HOP : next1;
877             }
878
879           vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
880           vnet_buffer (p1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
881
882           vlib_increment_combined_counter
883             (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
884           vlib_increment_combined_counter
885             (cm, thread_index, lbi1, 1, vlib_buffer_length_in_chain (vm, p1));
886
887           vlib_validate_buffer_enqueue_x2 (vm, node, next,
888                                            to_next, n_left_to_next,
889                                            pi0, pi1, next0, next1);
890         }
891
892       while (n_left_from > 0 && n_left_to_next > 0)
893         {
894           ip_lookup_next_t next0;
895           const load_balance_t *lb0;
896           vlib_buffer_t *p0;
897           u32 pi0, lbi0, hc0;
898           const ip6_header_t *ip0;
899           const dpo_id_t *dpo0;
900
901           pi0 = from[0];
902           to_next[0] = pi0;
903           from += 1;
904           to_next += 1;
905           n_left_to_next -= 1;
906           n_left_from -= 1;
907
908           p0 = vlib_get_buffer (vm, pi0);
909
910           ip0 = vlib_buffer_get_current (p0);
911           lbi0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
912
913           lb0 = load_balance_get (lbi0);
914
915           hc0 = 0;
916           if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
917             {
918               if (PREDICT_TRUE (vnet_buffer (p0)->ip.flow_hash))
919                 {
920                   hc0 = vnet_buffer (p0)->ip.flow_hash =
921                     vnet_buffer (p0)->ip.flow_hash >> 1;
922                 }
923               else
924                 {
925                   hc0 = vnet_buffer (p0)->ip.flow_hash =
926                     ip6_compute_flow_hash (ip0, lb0->lb_hash_config);
927                 }
928               dpo0 =
929                 load_balance_get_fwd_bucket (lb0,
930                                              (hc0 &
931                                               lb0->lb_n_buckets_minus_1));
932             }
933           else
934             {
935               dpo0 = load_balance_get_bucket_i (lb0, 0);
936             }
937
938           next0 = dpo0->dpoi_next_node;
939           vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
940
941           /* Only process the HBH Option Header if explicitly configured to do so */
942           if (PREDICT_FALSE
943               (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
944             {
945               next0 = (dpo_is_adj (dpo0) && im->hbh_enabled) ?
946                 (ip_lookup_next_t) IP6_LOOKUP_NEXT_HOP_BY_HOP : next0;
947             }
948
949           vlib_increment_combined_counter
950             (cm, thread_index, lbi0, 1, vlib_buffer_length_in_chain (vm, p0));
951
952           vlib_validate_buffer_enqueue_x1 (vm, node, next,
953                                            to_next, n_left_to_next,
954                                            pi0, next0);
955         }
956
957       vlib_put_next_frame (vm, node, next, n_left_to_next);
958     }
959
960   return frame->n_vectors;
961 }
962
963 /* *INDENT-OFF* */
964 VLIB_REGISTER_NODE (ip6_load_balance_node) =
965 {
966   .function = ip6_load_balance,
967   .name = "ip6-load-balance",
968   .vector_size = sizeof (u32),
969   .sibling_of = "ip6-lookup",
970   .format_trace = format_ip6_lookup_trace,
971 };
972 /* *INDENT-ON* */
973
974 VLIB_NODE_FUNCTION_MULTIARCH (ip6_load_balance_node, ip6_load_balance);
975
976 typedef struct
977 {
978   /* Adjacency taken. */
979   u32 adj_index;
980   u32 flow_hash;
981   u32 fib_index;
982
983   /* Packet data, possibly *after* rewrite. */
984   u8 packet_data[128 - 1 * sizeof (u32)];
985 }
986 ip6_forward_next_trace_t;
987
988 u8 *
989 format_ip6_forward_next_trace (u8 * s, va_list * args)
990 {
991   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
992   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
993   ip6_forward_next_trace_t *t = va_arg (*args, ip6_forward_next_trace_t *);
994   uword indent = format_get_indent (s);
995
996   s = format (s, "%U%U",
997               format_white_space, indent,
998               format_ip6_header, t->packet_data, sizeof (t->packet_data));
999   return s;
1000 }
1001
1002 static u8 *
1003 format_ip6_lookup_trace (u8 * s, va_list * args)
1004 {
1005   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1006   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1007   ip6_forward_next_trace_t *t = va_arg (*args, ip6_forward_next_trace_t *);
1008   uword indent = format_get_indent (s);
1009
1010   s = format (s, "fib %d dpo-idx %d flow hash: 0x%08x",
1011               t->fib_index, t->adj_index, t->flow_hash);
1012   s = format (s, "\n%U%U",
1013               format_white_space, indent,
1014               format_ip6_header, t->packet_data, sizeof (t->packet_data));
1015   return s;
1016 }
1017
1018
1019 static u8 *
1020 format_ip6_rewrite_trace (u8 * s, va_list * args)
1021 {
1022   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1023   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1024   ip6_forward_next_trace_t *t = va_arg (*args, ip6_forward_next_trace_t *);
1025   uword indent = format_get_indent (s);
1026
1027   s = format (s, "tx_sw_if_index %d adj-idx %d : %U flow hash: 0x%08x",
1028               t->fib_index, t->adj_index, format_ip_adjacency,
1029               t->adj_index, FORMAT_IP_ADJACENCY_NONE, t->flow_hash);
1030   s = format (s, "\n%U%U",
1031               format_white_space, indent,
1032               format_ip_adjacency_packet_data,
1033               t->adj_index, t->packet_data, sizeof (t->packet_data));
1034   return s;
1035 }
1036
1037 /* Common trace function for all ip6-forward next nodes. */
1038 void
1039 ip6_forward_next_trace (vlib_main_t * vm,
1040                         vlib_node_runtime_t * node,
1041                         vlib_frame_t * frame, vlib_rx_or_tx_t which_adj_index)
1042 {
1043   u32 *from, n_left;
1044   ip6_main_t *im = &ip6_main;
1045
1046   n_left = frame->n_vectors;
1047   from = vlib_frame_vector_args (frame);
1048
1049   while (n_left >= 4)
1050     {
1051       u32 bi0, bi1;
1052       vlib_buffer_t *b0, *b1;
1053       ip6_forward_next_trace_t *t0, *t1;
1054
1055       /* Prefetch next iteration. */
1056       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
1057       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
1058
1059       bi0 = from[0];
1060       bi1 = from[1];
1061
1062       b0 = vlib_get_buffer (vm, bi0);
1063       b1 = vlib_get_buffer (vm, bi1);
1064
1065       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1066         {
1067           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
1068           t0->adj_index = vnet_buffer (b0)->ip.adj_index[which_adj_index];
1069           t0->flow_hash = vnet_buffer (b0)->ip.flow_hash;
1070           t0->fib_index =
1071             (vnet_buffer (b0)->sw_if_index[VLIB_TX] !=
1072              (u32) ~ 0) ? vnet_buffer (b0)->sw_if_index[VLIB_TX] :
1073             vec_elt (im->fib_index_by_sw_if_index,
1074                      vnet_buffer (b0)->sw_if_index[VLIB_RX]);
1075
1076           clib_memcpy (t0->packet_data,
1077                        vlib_buffer_get_current (b0),
1078                        sizeof (t0->packet_data));
1079         }
1080       if (b1->flags & VLIB_BUFFER_IS_TRACED)
1081         {
1082           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
1083           t1->adj_index = vnet_buffer (b1)->ip.adj_index[which_adj_index];
1084           t1->flow_hash = vnet_buffer (b1)->ip.flow_hash;
1085           t1->fib_index =
1086             (vnet_buffer (b1)->sw_if_index[VLIB_TX] !=
1087              (u32) ~ 0) ? vnet_buffer (b1)->sw_if_index[VLIB_TX] :
1088             vec_elt (im->fib_index_by_sw_if_index,
1089                      vnet_buffer (b1)->sw_if_index[VLIB_RX]);
1090
1091           clib_memcpy (t1->packet_data,
1092                        vlib_buffer_get_current (b1),
1093                        sizeof (t1->packet_data));
1094         }
1095       from += 2;
1096       n_left -= 2;
1097     }
1098
1099   while (n_left >= 1)
1100     {
1101       u32 bi0;
1102       vlib_buffer_t *b0;
1103       ip6_forward_next_trace_t *t0;
1104
1105       bi0 = from[0];
1106
1107       b0 = vlib_get_buffer (vm, bi0);
1108
1109       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1110         {
1111           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
1112           t0->adj_index = vnet_buffer (b0)->ip.adj_index[which_adj_index];
1113           t0->flow_hash = vnet_buffer (b0)->ip.flow_hash;
1114           t0->fib_index =
1115             (vnet_buffer (b0)->sw_if_index[VLIB_TX] !=
1116              (u32) ~ 0) ? vnet_buffer (b0)->sw_if_index[VLIB_TX] :
1117             vec_elt (im->fib_index_by_sw_if_index,
1118                      vnet_buffer (b0)->sw_if_index[VLIB_RX]);
1119
1120           clib_memcpy (t0->packet_data,
1121                        vlib_buffer_get_current (b0),
1122                        sizeof (t0->packet_data));
1123         }
1124       from += 1;
1125       n_left -= 1;
1126     }
1127 }
1128
1129 static uword
1130 ip6_drop_or_punt (vlib_main_t * vm,
1131                   vlib_node_runtime_t * node,
1132                   vlib_frame_t * frame, ip6_error_t error_code)
1133 {
1134   u32 *buffers = vlib_frame_vector_args (frame);
1135   uword n_packets = frame->n_vectors;
1136
1137   vlib_error_drop_buffers (vm, node, buffers,
1138                            /* stride */ 1,
1139                            n_packets,
1140                            /* next */ 0,
1141                            ip6_input_node.index, error_code);
1142
1143   if (node->flags & VLIB_NODE_FLAG_TRACE)
1144     ip6_forward_next_trace (vm, node, frame, VLIB_TX);
1145
1146   return n_packets;
1147 }
1148
1149 static uword
1150 ip6_drop (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
1151 {
1152   return ip6_drop_or_punt (vm, node, frame, IP6_ERROR_ADJACENCY_DROP);
1153 }
1154
1155 static uword
1156 ip6_punt (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
1157 {
1158   return ip6_drop_or_punt (vm, node, frame, IP6_ERROR_ADJACENCY_PUNT);
1159 }
1160
1161 /* *INDENT-OFF* */
1162 VLIB_REGISTER_NODE (ip6_drop_node, static) =
1163 {
1164   .function = ip6_drop,
1165   .name = "ip6-drop",
1166   .vector_size = sizeof (u32),
1167   .format_trace = format_ip6_forward_next_trace,
1168   .n_next_nodes = 1,
1169   .next_nodes =
1170   {
1171     [0] = "error-drop",},
1172 };
1173 /* *INDENT-ON* */
1174
1175 VLIB_NODE_FUNCTION_MULTIARCH (ip6_drop_node, ip6_drop);
1176
1177 /* *INDENT-OFF* */
1178 VLIB_REGISTER_NODE (ip6_punt_node, static) =
1179 {
1180   .function = ip6_punt,
1181   .name = "ip6-punt",
1182   .vector_size = sizeof (u32),
1183   .format_trace = format_ip6_forward_next_trace,
1184   .n_next_nodes = 1,
1185   .next_nodes =
1186   {
1187     [0] = "error-punt",},
1188 };
1189 /* *INDENT-ON* */
1190
1191 VLIB_NODE_FUNCTION_MULTIARCH (ip6_punt_node, ip6_punt);
1192
1193 /* Compute TCP/UDP/ICMP6 checksum in software. */
1194 u16
1195 ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
1196                                    ip6_header_t * ip0, int *bogus_lengthp)
1197 {
1198   ip_csum_t sum0;
1199   u16 sum16, payload_length_host_byte_order;
1200   u32 i, n_this_buffer, n_bytes_left;
1201   u32 headers_size = sizeof (ip0[0]);
1202   void *data_this_buffer;
1203
1204   ASSERT (bogus_lengthp);
1205   *bogus_lengthp = 0;
1206
1207   /* Initialize checksum with ip header. */
1208   sum0 = ip0->payload_length + clib_host_to_net_u16 (ip0->protocol);
1209   payload_length_host_byte_order = clib_net_to_host_u16 (ip0->payload_length);
1210   data_this_buffer = (void *) (ip0 + 1);
1211
1212   for (i = 0; i < ARRAY_LEN (ip0->src_address.as_uword); i++)
1213     {
1214       sum0 = ip_csum_with_carry (sum0,
1215                                  clib_mem_unaligned (&ip0->
1216                                                      src_address.as_uword[i],
1217                                                      uword));
1218       sum0 =
1219         ip_csum_with_carry (sum0,
1220                             clib_mem_unaligned (&ip0->dst_address.as_uword[i],
1221                                                 uword));
1222     }
1223
1224   /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets)
1225    * or UDP-Ping packets */
1226   if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS))
1227     {
1228       u32 skip_bytes;
1229       ip6_hop_by_hop_ext_t *ext_hdr =
1230         (ip6_hop_by_hop_ext_t *) data_this_buffer;
1231
1232       /* validate really icmp6 next */
1233       ASSERT ((ext_hdr->next_hdr == IP_PROTOCOL_ICMP6)
1234               || (ext_hdr->next_hdr == IP_PROTOCOL_UDP));
1235
1236       skip_bytes = 8 * (1 + ext_hdr->n_data_u64s);
1237       data_this_buffer = (void *) ((u8 *) data_this_buffer + skip_bytes);
1238
1239       payload_length_host_byte_order -= skip_bytes;
1240       headers_size += skip_bytes;
1241     }
1242
1243   n_bytes_left = n_this_buffer = payload_length_host_byte_order;
1244   if (p0 && n_this_buffer + headers_size > p0->current_length)
1245     n_this_buffer =
1246       p0->current_length >
1247       headers_size ? p0->current_length - headers_size : 0;
1248   while (1)
1249     {
1250       sum0 = ip_incremental_checksum (sum0, data_this_buffer, n_this_buffer);
1251       n_bytes_left -= n_this_buffer;
1252       if (n_bytes_left == 0)
1253         break;
1254
1255       if (!(p0->flags & VLIB_BUFFER_NEXT_PRESENT))
1256         {
1257           *bogus_lengthp = 1;
1258           return 0xfefe;
1259         }
1260       p0 = vlib_get_buffer (vm, p0->next_buffer);
1261       data_this_buffer = vlib_buffer_get_current (p0);
1262       n_this_buffer = p0->current_length;
1263     }
1264
1265   sum16 = ~ip_csum_fold (sum0);
1266
1267   return sum16;
1268 }
1269
1270 u32
1271 ip6_tcp_udp_icmp_validate_checksum (vlib_main_t * vm, vlib_buffer_t * p0)
1272 {
1273   ip6_header_t *ip0 = vlib_buffer_get_current (p0);
1274   udp_header_t *udp0;
1275   u16 sum16;
1276   int bogus_length;
1277
1278   /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets) */
1279   ASSERT (ip0->protocol == IP_PROTOCOL_TCP
1280           || ip0->protocol == IP_PROTOCOL_ICMP6
1281           || ip0->protocol == IP_PROTOCOL_UDP
1282           || ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS);
1283
1284   udp0 = (void *) (ip0 + 1);
1285   if (ip0->protocol == IP_PROTOCOL_UDP && udp0->checksum == 0)
1286     {
1287       p0->flags |= (IP_BUFFER_L4_CHECKSUM_COMPUTED
1288                     | IP_BUFFER_L4_CHECKSUM_CORRECT);
1289       return p0->flags;
1290     }
1291
1292   sum16 = ip6_tcp_udp_icmp_compute_checksum (vm, p0, ip0, &bogus_length);
1293
1294   p0->flags |= (IP_BUFFER_L4_CHECKSUM_COMPUTED
1295                 | ((sum16 == 0) << LOG2_IP_BUFFER_L4_CHECKSUM_CORRECT));
1296
1297   return p0->flags;
1298 }
1299
1300 /**
1301  * @brief returns number of links on which src is reachable.
1302  */
1303 always_inline int
1304 ip6_urpf_loose_check (ip6_main_t * im, vlib_buffer_t * b, ip6_header_t * i)
1305 {
1306   const load_balance_t *lb0;
1307   index_t lbi;
1308
1309   lbi = ip6_fib_table_fwding_lookup_with_if_index (im,
1310                                                    vnet_buffer
1311                                                    (b)->sw_if_index[VLIB_RX],
1312                                                    &i->src_address);
1313
1314   lb0 = load_balance_get (lbi);
1315
1316   return (fib_urpf_check_size (lb0->lb_urpf));
1317 }
1318
1319 static uword
1320 ip6_local (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
1321 {
1322   ip6_main_t *im = &ip6_main;
1323   ip_lookup_main_t *lm = &im->lookup_main;
1324   ip_local_next_t next_index;
1325   u32 *from, *to_next, n_left_from, n_left_to_next;
1326   vlib_node_runtime_t *error_node =
1327     vlib_node_get_runtime (vm, ip6_input_node.index);
1328
1329   from = vlib_frame_vector_args (frame);
1330   n_left_from = frame->n_vectors;
1331   next_index = node->cached_next_index;
1332
1333   if (node->flags & VLIB_NODE_FLAG_TRACE)
1334     ip6_forward_next_trace (vm, node, frame, VLIB_TX);
1335
1336   while (n_left_from > 0)
1337     {
1338       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1339
1340       while (n_left_from >= 4 && n_left_to_next >= 2)
1341         {
1342           vlib_buffer_t *p0, *p1;
1343           ip6_header_t *ip0, *ip1;
1344           udp_header_t *udp0, *udp1;
1345           u32 pi0, ip_len0, udp_len0, flags0, next0;
1346           u32 pi1, ip_len1, udp_len1, flags1, next1;
1347           i32 len_diff0, len_diff1;
1348           u8 error0, type0, good_l4_checksum0;
1349           u8 error1, type1, good_l4_checksum1;
1350           u32 udp_offset0, udp_offset1;
1351
1352           pi0 = to_next[0] = from[0];
1353           pi1 = to_next[1] = from[1];
1354           from += 2;
1355           n_left_from -= 2;
1356           to_next += 2;
1357           n_left_to_next -= 2;
1358
1359           p0 = vlib_get_buffer (vm, pi0);
1360           p1 = vlib_get_buffer (vm, pi1);
1361
1362           ip0 = vlib_buffer_get_current (p0);
1363           ip1 = vlib_buffer_get_current (p1);
1364
1365           vnet_buffer (p0)->ip.start_of_ip_header = p0->current_data;
1366           vnet_buffer (p1)->ip.start_of_ip_header = p1->current_data;
1367
1368           type0 = lm->builtin_protocol_by_ip_protocol[ip0->protocol];
1369           type1 = lm->builtin_protocol_by_ip_protocol[ip1->protocol];
1370
1371           next0 = lm->local_next_by_ip_protocol[ip0->protocol];
1372           next1 = lm->local_next_by_ip_protocol[ip1->protocol];
1373
1374           flags0 = p0->flags;
1375           flags1 = p1->flags;
1376
1377           good_l4_checksum0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
1378           good_l4_checksum1 = (flags1 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
1379           len_diff0 = 0;
1380           len_diff1 = 0;
1381
1382           if (PREDICT_TRUE (IP_PROTOCOL_UDP == ip6_locate_header (p0, ip0,
1383                                                                   IP_PROTOCOL_UDP,
1384                                                                   &udp_offset0)))
1385             {
1386               udp0 = (udp_header_t *) ((u8 *) ip0 + udp_offset0);
1387               /* Don't verify UDP checksum for packets with explicit zero checksum. */
1388               good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UDP
1389                 && udp0->checksum == 0;
1390               /* Verify UDP length. */
1391               ip_len0 = clib_net_to_host_u16 (ip0->payload_length);
1392               udp_len0 = clib_net_to_host_u16 (udp0->length);
1393               len_diff0 = ip_len0 - udp_len0;
1394             }
1395           if (PREDICT_TRUE (IP_PROTOCOL_UDP == ip6_locate_header (p1, ip1,
1396                                                                   IP_PROTOCOL_UDP,
1397                                                                   &udp_offset1)))
1398             {
1399               udp1 = (udp_header_t *) ((u8 *) ip1 + udp_offset1);
1400               /* Don't verify UDP checksum for packets with explicit zero checksum. */
1401               good_l4_checksum1 |= type1 == IP_BUILTIN_PROTOCOL_UDP
1402                 && udp1->checksum == 0;
1403               /* Verify UDP length. */
1404               ip_len1 = clib_net_to_host_u16 (ip1->payload_length);
1405               udp_len1 = clib_net_to_host_u16 (udp1->length);
1406               len_diff1 = ip_len1 - udp_len1;
1407             }
1408
1409           good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UNKNOWN;
1410           good_l4_checksum1 |= type1 == IP_BUILTIN_PROTOCOL_UNKNOWN;
1411
1412           len_diff0 = type0 == IP_BUILTIN_PROTOCOL_UDP ? len_diff0 : 0;
1413           len_diff1 = type1 == IP_BUILTIN_PROTOCOL_UDP ? len_diff1 : 0;
1414
1415           if (PREDICT_FALSE (type0 != IP_BUILTIN_PROTOCOL_UNKNOWN
1416                              && !good_l4_checksum0
1417                              && !(flags0 & IP_BUFFER_L4_CHECKSUM_COMPUTED)))
1418             {
1419               flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, p0);
1420               good_l4_checksum0 =
1421                 (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
1422             }
1423           if (PREDICT_FALSE (type1 != IP_BUILTIN_PROTOCOL_UNKNOWN
1424                              && !good_l4_checksum1
1425                              && !(flags1 & IP_BUFFER_L4_CHECKSUM_COMPUTED)))
1426             {
1427               flags1 = ip6_tcp_udp_icmp_validate_checksum (vm, p1);
1428               good_l4_checksum1 =
1429                 (flags1 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
1430             }
1431
1432           error0 = error1 = IP6_ERROR_UNKNOWN_PROTOCOL;
1433
1434           error0 = len_diff0 < 0 ? IP6_ERROR_UDP_LENGTH : error0;
1435           error1 = len_diff1 < 0 ? IP6_ERROR_UDP_LENGTH : error1;
1436
1437           ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_UDP ==
1438                   IP6_ERROR_UDP_CHECKSUM);
1439           ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_ICMP ==
1440                   IP6_ERROR_ICMP_CHECKSUM);
1441           error0 =
1442             (!good_l4_checksum0 ? IP6_ERROR_UDP_CHECKSUM + type0 : error0);
1443           error1 =
1444             (!good_l4_checksum1 ? IP6_ERROR_UDP_CHECKSUM + type1 : error1);
1445
1446           /* Drop packets from unroutable hosts. */
1447           /* If this is a neighbor solicitation (ICMP), skip source RPF check */
1448           if (error0 == IP6_ERROR_UNKNOWN_PROTOCOL &&
1449               type0 != IP_BUILTIN_PROTOCOL_ICMP &&
1450               !ip6_address_is_link_local_unicast (&ip0->src_address))
1451             {
1452               error0 = (!ip6_urpf_loose_check (im, p0, ip0)
1453                         ? IP6_ERROR_SRC_LOOKUP_MISS : error0);
1454             }
1455           if (error1 == IP6_ERROR_UNKNOWN_PROTOCOL &&
1456               type1 != IP_BUILTIN_PROTOCOL_ICMP &&
1457               !ip6_address_is_link_local_unicast (&ip1->src_address))
1458             {
1459               error1 = (!ip6_urpf_loose_check (im, p1, ip1)
1460                         ? IP6_ERROR_SRC_LOOKUP_MISS : error1);
1461             }
1462
1463           next0 =
1464             error0 != IP6_ERROR_UNKNOWN_PROTOCOL ? IP_LOCAL_NEXT_DROP : next0;
1465           next1 =
1466             error1 != IP6_ERROR_UNKNOWN_PROTOCOL ? IP_LOCAL_NEXT_DROP : next1;
1467
1468           p0->error = error_node->errors[error0];
1469           p1->error = error_node->errors[error1];
1470
1471           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1472                                            to_next, n_left_to_next,
1473                                            pi0, pi1, next0, next1);
1474         }
1475
1476       while (n_left_from > 0 && n_left_to_next > 0)
1477         {
1478           vlib_buffer_t *p0;
1479           ip6_header_t *ip0;
1480           udp_header_t *udp0;
1481           u32 pi0, ip_len0, udp_len0, flags0, next0;
1482           i32 len_diff0;
1483           u8 error0, type0, good_l4_checksum0;
1484           u32 udp_offset0;
1485
1486           pi0 = to_next[0] = from[0];
1487           from += 1;
1488           n_left_from -= 1;
1489           to_next += 1;
1490           n_left_to_next -= 1;
1491
1492           p0 = vlib_get_buffer (vm, pi0);
1493
1494           ip0 = vlib_buffer_get_current (p0);
1495
1496           vnet_buffer (p0)->ip.start_of_ip_header = p0->current_data;
1497
1498           type0 = lm->builtin_protocol_by_ip_protocol[ip0->protocol];
1499           next0 = lm->local_next_by_ip_protocol[ip0->protocol];
1500
1501           flags0 = p0->flags;
1502
1503           good_l4_checksum0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
1504           len_diff0 = 0;
1505
1506           if (PREDICT_TRUE (IP_PROTOCOL_UDP == ip6_locate_header (p0, ip0,
1507                                                                   IP_PROTOCOL_UDP,
1508                                                                   &udp_offset0)))
1509             {
1510               udp0 = (udp_header_t *) ((u8 *) ip0 + udp_offset0);
1511               /* Don't verify UDP checksum for packets with explicit zero checksum. */
1512               good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UDP
1513                 && udp0->checksum == 0;
1514               /* Verify UDP length. */
1515               ip_len0 = clib_net_to_host_u16 (ip0->payload_length);
1516               udp_len0 = clib_net_to_host_u16 (udp0->length);
1517               len_diff0 = ip_len0 - udp_len0;
1518             }
1519
1520           good_l4_checksum0 |= type0 == IP_BUILTIN_PROTOCOL_UNKNOWN;
1521           len_diff0 = type0 == IP_BUILTIN_PROTOCOL_UDP ? len_diff0 : 0;
1522
1523           if (PREDICT_FALSE (type0 != IP_BUILTIN_PROTOCOL_UNKNOWN
1524                              && !good_l4_checksum0
1525                              && !(flags0 & IP_BUFFER_L4_CHECKSUM_COMPUTED)))
1526             {
1527               flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, p0);
1528               good_l4_checksum0 =
1529                 (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0;
1530             }
1531
1532           error0 = IP6_ERROR_UNKNOWN_PROTOCOL;
1533
1534           error0 = len_diff0 < 0 ? IP6_ERROR_UDP_LENGTH : error0;
1535
1536           ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_UDP ==
1537                   IP6_ERROR_UDP_CHECKSUM);
1538           ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_ICMP ==
1539                   IP6_ERROR_ICMP_CHECKSUM);
1540           error0 =
1541             (!good_l4_checksum0 ? IP6_ERROR_UDP_CHECKSUM + type0 : error0);
1542
1543           /* If this is a neighbor solicitation (ICMP), skip source RPF check */
1544           if (error0 == IP6_ERROR_UNKNOWN_PROTOCOL &&
1545               type0 != IP_BUILTIN_PROTOCOL_ICMP &&
1546               !ip6_address_is_link_local_unicast (&ip0->src_address))
1547             {
1548               error0 = (!ip6_urpf_loose_check (im, p0, ip0)
1549                         ? IP6_ERROR_SRC_LOOKUP_MISS : error0);
1550             }
1551
1552           next0 =
1553             error0 != IP6_ERROR_UNKNOWN_PROTOCOL ? IP_LOCAL_NEXT_DROP : next0;
1554
1555           p0->error = error_node->errors[error0];
1556
1557           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1558                                            to_next, n_left_to_next,
1559                                            pi0, next0);
1560         }
1561
1562       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1563     }
1564
1565   return frame->n_vectors;
1566 }
1567
1568 /* *INDENT-OFF* */
1569 VLIB_REGISTER_NODE (ip6_local_node, static) =
1570 {
1571   .function = ip6_local,
1572   .name = "ip6-local",
1573   .vector_size = sizeof (u32),
1574   .format_trace = format_ip6_forward_next_trace,
1575   .n_next_nodes = IP_LOCAL_N_NEXT,
1576   .next_nodes =
1577   {
1578     [IP_LOCAL_NEXT_DROP] = "error-drop",
1579     [IP_LOCAL_NEXT_PUNT] = "error-punt",
1580     [IP_LOCAL_NEXT_UDP_LOOKUP] = "ip6-udp-lookup",
1581     [IP_LOCAL_NEXT_ICMP] = "ip6-icmp-input",
1582   },
1583 };
1584 /* *INDENT-ON* */
1585
1586 VLIB_NODE_FUNCTION_MULTIARCH (ip6_local_node, ip6_local);
1587
1588 void
1589 ip6_register_protocol (u32 protocol, u32 node_index)
1590 {
1591   vlib_main_t *vm = vlib_get_main ();
1592   ip6_main_t *im = &ip6_main;
1593   ip_lookup_main_t *lm = &im->lookup_main;
1594
1595   ASSERT (protocol < ARRAY_LEN (lm->local_next_by_ip_protocol));
1596   lm->local_next_by_ip_protocol[protocol] =
1597     vlib_node_add_next (vm, ip6_local_node.index, node_index);
1598 }
1599
1600 typedef enum
1601 {
1602   IP6_DISCOVER_NEIGHBOR_NEXT_DROP,
1603   IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX,
1604   IP6_DISCOVER_NEIGHBOR_N_NEXT,
1605 } ip6_discover_neighbor_next_t;
1606
1607 typedef enum
1608 {
1609   IP6_DISCOVER_NEIGHBOR_ERROR_DROP,
1610   IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT,
1611   IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS,
1612 } ip6_discover_neighbor_error_t;
1613
1614 static uword
1615 ip6_discover_neighbor_inline (vlib_main_t * vm,
1616                               vlib_node_runtime_t * node,
1617                               vlib_frame_t * frame, int is_glean)
1618 {
1619   vnet_main_t *vnm = vnet_get_main ();
1620   ip6_main_t *im = &ip6_main;
1621   ip_lookup_main_t *lm = &im->lookup_main;
1622   u32 *from, *to_next_drop;
1623   uword n_left_from, n_left_to_next_drop;
1624   static f64 time_last_seed_change = -1e100;
1625   static u32 hash_seeds[3];
1626   static uword hash_bitmap[256 / BITS (uword)];
1627   f64 time_now;
1628   int bogus_length;
1629
1630   if (node->flags & VLIB_NODE_FLAG_TRACE)
1631     ip6_forward_next_trace (vm, node, frame, VLIB_TX);
1632
1633   time_now = vlib_time_now (vm);
1634   if (time_now - time_last_seed_change > 1e-3)
1635     {
1636       uword i;
1637       u32 *r = clib_random_buffer_get_data (&vm->random_buffer,
1638                                             sizeof (hash_seeds));
1639       for (i = 0; i < ARRAY_LEN (hash_seeds); i++)
1640         hash_seeds[i] = r[i];
1641
1642       /* Mark all hash keys as been not-seen before. */
1643       for (i = 0; i < ARRAY_LEN (hash_bitmap); i++)
1644         hash_bitmap[i] = 0;
1645
1646       time_last_seed_change = time_now;
1647     }
1648
1649   from = vlib_frame_vector_args (frame);
1650   n_left_from = frame->n_vectors;
1651
1652   while (n_left_from > 0)
1653     {
1654       vlib_get_next_frame (vm, node, IP6_DISCOVER_NEIGHBOR_NEXT_DROP,
1655                            to_next_drop, n_left_to_next_drop);
1656
1657       while (n_left_from > 0 && n_left_to_next_drop > 0)
1658         {
1659           vlib_buffer_t *p0;
1660           ip6_header_t *ip0;
1661           u32 pi0, adj_index0, a0, b0, c0, m0, sw_if_index0, drop0;
1662           uword bm0;
1663           ip_adjacency_t *adj0;
1664           vnet_hw_interface_t *hw_if0;
1665           u32 next0;
1666
1667           pi0 = from[0];
1668
1669           p0 = vlib_get_buffer (vm, pi0);
1670
1671           adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
1672
1673           ip0 = vlib_buffer_get_current (p0);
1674
1675           adj0 = adj_get (adj_index0);
1676
1677           if (!is_glean)
1678             {
1679               ip0->dst_address.as_u64[0] =
1680                 adj0->sub_type.nbr.next_hop.ip6.as_u64[0];
1681               ip0->dst_address.as_u64[1] =
1682                 adj0->sub_type.nbr.next_hop.ip6.as_u64[1];
1683             }
1684
1685           a0 = hash_seeds[0];
1686           b0 = hash_seeds[1];
1687           c0 = hash_seeds[2];
1688
1689           sw_if_index0 = adj0->rewrite_header.sw_if_index;
1690           vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1691
1692           a0 ^= sw_if_index0;
1693           b0 ^= ip0->dst_address.as_u32[0];
1694           c0 ^= ip0->dst_address.as_u32[1];
1695
1696           hash_v3_mix32 (a0, b0, c0);
1697
1698           b0 ^= ip0->dst_address.as_u32[2];
1699           c0 ^= ip0->dst_address.as_u32[3];
1700
1701           hash_v3_finalize32 (a0, b0, c0);
1702
1703           c0 &= BITS (hash_bitmap) - 1;
1704           c0 = c0 / BITS (uword);
1705           m0 = (uword) 1 << (c0 % BITS (uword));
1706
1707           bm0 = hash_bitmap[c0];
1708           drop0 = (bm0 & m0) != 0;
1709
1710           /* Mark it as seen. */
1711           hash_bitmap[c0] = bm0 | m0;
1712
1713           from += 1;
1714           n_left_from -= 1;
1715           to_next_drop[0] = pi0;
1716           to_next_drop += 1;
1717           n_left_to_next_drop -= 1;
1718
1719           hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1720
1721           /* If the interface is link-down, drop the pkt */
1722           if (!(hw_if0->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
1723             drop0 = 1;
1724
1725           p0->error =
1726             node->errors[drop0 ? IP6_DISCOVER_NEIGHBOR_ERROR_DROP
1727                          : IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT];
1728           if (drop0)
1729             continue;
1730
1731           /*
1732            * the adj has been updated to a rewrite but the node the DPO that got
1733            * us here hasn't - yet. no big deal. we'll drop while we wait.
1734            */
1735           if (IP_LOOKUP_NEXT_REWRITE == adj0->lookup_next_index)
1736             continue;
1737
1738           {
1739             u32 bi0 = 0;
1740             icmp6_neighbor_solicitation_header_t *h0;
1741             vlib_buffer_t *b0;
1742
1743             h0 = vlib_packet_template_get_packet
1744               (vm, &im->discover_neighbor_packet_template, &bi0);
1745
1746             /*
1747              * Build ethernet header.
1748              * Choose source address based on destination lookup
1749              * adjacency.
1750              */
1751             if (ip6_src_address_for_packet (lm,
1752                                             sw_if_index0,
1753                                             &h0->ip.src_address))
1754               {
1755                 /* There is no address on the interface */
1756                 p0->error =
1757                   node->errors[IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS];
1758                 vlib_buffer_free (vm, &bi0, 1);
1759                 continue;
1760               }
1761
1762             /*
1763              * Destination address is a solicited node multicast address.
1764              * We need to fill in
1765              * the low 24 bits with low 24 bits of target's address.
1766              */
1767             h0->ip.dst_address.as_u8[13] = ip0->dst_address.as_u8[13];
1768             h0->ip.dst_address.as_u8[14] = ip0->dst_address.as_u8[14];
1769             h0->ip.dst_address.as_u8[15] = ip0->dst_address.as_u8[15];
1770
1771             h0->neighbor.target_address = ip0->dst_address;
1772
1773             clib_memcpy (h0->link_layer_option.ethernet_address,
1774                          hw_if0->hw_address, vec_len (hw_if0->hw_address));
1775
1776             /* $$$$ appears we need this; why is the checksum non-zero? */
1777             h0->neighbor.icmp.checksum = 0;
1778             h0->neighbor.icmp.checksum =
1779               ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip,
1780                                                  &bogus_length);
1781
1782             ASSERT (bogus_length == 0);
1783
1784             vlib_buffer_copy_trace_flag (vm, p0, bi0);
1785             b0 = vlib_get_buffer (vm, bi0);
1786             vnet_buffer (b0)->sw_if_index[VLIB_TX]
1787               = vnet_buffer (p0)->sw_if_index[VLIB_TX];
1788
1789             /* Add rewrite/encap string. */
1790             vnet_rewrite_one_header (adj0[0], h0, sizeof (ethernet_header_t));
1791             vlib_buffer_advance (b0, -adj0->rewrite_header.data_bytes);
1792
1793             next0 = IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX;
1794
1795             vlib_set_next_frame_buffer (vm, node, next0, bi0);
1796           }
1797         }
1798
1799       vlib_put_next_frame (vm, node, IP6_DISCOVER_NEIGHBOR_NEXT_DROP,
1800                            n_left_to_next_drop);
1801     }
1802
1803   return frame->n_vectors;
1804 }
1805
1806 static uword
1807 ip6_discover_neighbor (vlib_main_t * vm,
1808                        vlib_node_runtime_t * node, vlib_frame_t * frame)
1809 {
1810   return (ip6_discover_neighbor_inline (vm, node, frame, 0));
1811 }
1812
1813 static uword
1814 ip6_glean (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
1815 {
1816   return (ip6_discover_neighbor_inline (vm, node, frame, 1));
1817 }
1818
1819 static char *ip6_discover_neighbor_error_strings[] = {
1820   [IP6_DISCOVER_NEIGHBOR_ERROR_DROP] = "address overflow drops",
1821   [IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT] = "neighbor solicitations sent",
1822   [IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS]
1823     = "no source address for ND solicitation",
1824 };
1825
1826 /* *INDENT-OFF* */
1827 VLIB_REGISTER_NODE (ip6_discover_neighbor_node) =
1828 {
1829   .function = ip6_discover_neighbor,
1830   .name = "ip6-discover-neighbor",
1831   .vector_size = sizeof (u32),
1832   .format_trace = format_ip6_forward_next_trace,
1833   .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings),
1834   .error_strings = ip6_discover_neighbor_error_strings,
1835   .n_next_nodes = IP6_DISCOVER_NEIGHBOR_N_NEXT,
1836   .next_nodes =
1837   {
1838     [IP6_DISCOVER_NEIGHBOR_NEXT_DROP] = "error-drop",
1839     [IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX] = "interface-output",
1840   },
1841 };
1842 /* *INDENT-ON* */
1843
1844 /* *INDENT-OFF* */
1845 VLIB_REGISTER_NODE (ip6_glean_node) =
1846 {
1847   .function = ip6_glean,
1848   .name = "ip6-glean",
1849   .vector_size = sizeof (u32),
1850   .format_trace = format_ip6_forward_next_trace,
1851   .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings),
1852   .error_strings = ip6_discover_neighbor_error_strings,
1853   .n_next_nodes = IP6_DISCOVER_NEIGHBOR_N_NEXT,
1854   .next_nodes =
1855   {
1856     [IP6_DISCOVER_NEIGHBOR_NEXT_DROP] = "error-drop",
1857     [IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX] = "interface-output",
1858   },
1859 };
1860 /* *INDENT-ON* */
1861
1862 clib_error_t *
1863 ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, u32 sw_if_index)
1864 {
1865   vnet_main_t *vnm = vnet_get_main ();
1866   ip6_main_t *im = &ip6_main;
1867   icmp6_neighbor_solicitation_header_t *h;
1868   ip6_address_t *src;
1869   ip_interface_address_t *ia;
1870   ip_adjacency_t *adj;
1871   vnet_hw_interface_t *hi;
1872   vnet_sw_interface_t *si;
1873   vlib_buffer_t *b;
1874   adj_index_t ai;
1875   u32 bi = 0;
1876   int bogus_length;
1877
1878   si = vnet_get_sw_interface (vnm, sw_if_index);
1879
1880   if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
1881     {
1882       return clib_error_return (0, "%U: interface %U down",
1883                                 format_ip6_address, dst,
1884                                 format_vnet_sw_if_index_name, vnm,
1885                                 sw_if_index);
1886     }
1887
1888   src =
1889     ip6_interface_address_matching_destination (im, dst, sw_if_index, &ia);
1890   if (!src)
1891     {
1892       vnm->api_errno = VNET_API_ERROR_NO_MATCHING_INTERFACE;
1893       return clib_error_return
1894         (0, "no matching interface address for destination %U (interface %U)",
1895          format_ip6_address, dst,
1896          format_vnet_sw_if_index_name, vnm, sw_if_index);
1897     }
1898
1899   h =
1900     vlib_packet_template_get_packet (vm,
1901                                      &im->discover_neighbor_packet_template,
1902                                      &bi);
1903
1904   hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1905
1906   /* Destination address is a solicited node multicast address.  We need to fill in
1907      the low 24 bits with low 24 bits of target's address. */
1908   h->ip.dst_address.as_u8[13] = dst->as_u8[13];
1909   h->ip.dst_address.as_u8[14] = dst->as_u8[14];
1910   h->ip.dst_address.as_u8[15] = dst->as_u8[15];
1911
1912   h->ip.src_address = src[0];
1913   h->neighbor.target_address = dst[0];
1914
1915   clib_memcpy (h->link_layer_option.ethernet_address, hi->hw_address,
1916                vec_len (hi->hw_address));
1917
1918   h->neighbor.icmp.checksum =
1919     ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h->ip, &bogus_length);
1920   ASSERT (bogus_length == 0);
1921
1922   b = vlib_get_buffer (vm, bi);
1923   vnet_buffer (b)->sw_if_index[VLIB_RX] =
1924     vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
1925
1926   /* Add encapsulation string for software interface (e.g. ethernet header). */
1927   ip46_address_t nh = {
1928     .ip6 = *dst,
1929   };
1930
1931   ai = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
1932                             VNET_LINK_IP6, &nh, sw_if_index);
1933   adj = adj_get (ai);
1934
1935   vnet_rewrite_one_header (adj[0], h, sizeof (ethernet_header_t));
1936   vlib_buffer_advance (b, -adj->rewrite_header.data_bytes);
1937
1938   {
1939     vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
1940     u32 *to_next = vlib_frame_vector_args (f);
1941     to_next[0] = bi;
1942     f->n_vectors = 1;
1943     vlib_put_frame_to_node (vm, hi->output_node_index, f);
1944   }
1945
1946   adj_unlock (ai);
1947   return /* no error */ 0;
1948 }
1949
1950 typedef enum
1951 {
1952   IP6_REWRITE_NEXT_DROP,
1953   IP6_REWRITE_NEXT_ICMP_ERROR,
1954 } ip6_rewrite_next_t;
1955
1956 always_inline uword
1957 ip6_rewrite_inline (vlib_main_t * vm,
1958                     vlib_node_runtime_t * node,
1959                     vlib_frame_t * frame,
1960                     int do_counters, int is_midchain, int is_mcast)
1961 {
1962   ip_lookup_main_t *lm = &ip6_main.lookup_main;
1963   u32 *from = vlib_frame_vector_args (frame);
1964   u32 n_left_from, n_left_to_next, *to_next, next_index;
1965   vlib_node_runtime_t *error_node =
1966     vlib_node_get_runtime (vm, ip6_input_node.index);
1967
1968   n_left_from = frame->n_vectors;
1969   next_index = node->cached_next_index;
1970   u32 thread_index = vlib_get_thread_index ();
1971
1972   while (n_left_from > 0)
1973     {
1974       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1975
1976       while (n_left_from >= 4 && n_left_to_next >= 2)
1977         {
1978           ip_adjacency_t *adj0, *adj1;
1979           vlib_buffer_t *p0, *p1;
1980           ip6_header_t *ip0, *ip1;
1981           u32 pi0, rw_len0, next0, error0, adj_index0;
1982           u32 pi1, rw_len1, next1, error1, adj_index1;
1983           u32 tx_sw_if_index0, tx_sw_if_index1;
1984
1985           /* Prefetch next iteration. */
1986           {
1987             vlib_buffer_t *p2, *p3;
1988
1989             p2 = vlib_get_buffer (vm, from[2]);
1990             p3 = vlib_get_buffer (vm, from[3]);
1991
1992             vlib_prefetch_buffer_header (p2, LOAD);
1993             vlib_prefetch_buffer_header (p3, LOAD);
1994
1995             CLIB_PREFETCH (p2->pre_data, 32, STORE);
1996             CLIB_PREFETCH (p3->pre_data, 32, STORE);
1997
1998             CLIB_PREFETCH (p2->data, sizeof (ip0[0]), STORE);
1999             CLIB_PREFETCH (p3->data, sizeof (ip0[0]), STORE);
2000           }
2001
2002           pi0 = to_next[0] = from[0];
2003           pi1 = to_next[1] = from[1];
2004
2005           from += 2;
2006           n_left_from -= 2;
2007           to_next += 2;
2008           n_left_to_next -= 2;
2009
2010           p0 = vlib_get_buffer (vm, pi0);
2011           p1 = vlib_get_buffer (vm, pi1);
2012
2013           adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
2014           adj_index1 = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
2015
2016           ip0 = vlib_buffer_get_current (p0);
2017           ip1 = vlib_buffer_get_current (p1);
2018
2019           error0 = error1 = IP6_ERROR_NONE;
2020           next0 = next1 = IP6_REWRITE_NEXT_DROP;
2021
2022           if (PREDICT_TRUE (!(p0->flags & VNET_BUFFER_LOCALLY_ORIGINATED)))
2023             {
2024               i32 hop_limit0 = ip0->hop_limit;
2025
2026               /* Input node should have reject packets with hop limit 0. */
2027               ASSERT (ip0->hop_limit > 0);
2028
2029               hop_limit0 -= 1;
2030
2031               ip0->hop_limit = hop_limit0;
2032
2033               /*
2034                * If the hop count drops below 1 when forwarding, generate
2035                * an ICMP response.
2036                */
2037               if (PREDICT_FALSE (hop_limit0 <= 0))
2038                 {
2039                   error0 = IP6_ERROR_TIME_EXPIRED;
2040                   next0 = IP6_REWRITE_NEXT_ICMP_ERROR;
2041                   vnet_buffer (p0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
2042                   icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
2043                                                ICMP6_time_exceeded_ttl_exceeded_in_transit,
2044                                                0);
2045                 }
2046             }
2047           else
2048             {
2049               p0->flags &= ~VNET_BUFFER_LOCALLY_ORIGINATED;
2050             }
2051           if (PREDICT_TRUE (!(p1->flags & VNET_BUFFER_LOCALLY_ORIGINATED)))
2052             {
2053               i32 hop_limit1 = ip1->hop_limit;
2054
2055               /* Input node should have reject packets with hop limit 0. */
2056               ASSERT (ip1->hop_limit > 0);
2057
2058               hop_limit1 -= 1;
2059
2060               ip1->hop_limit = hop_limit1;
2061
2062               /*
2063                * If the hop count drops below 1 when forwarding, generate
2064                * an ICMP response.
2065                */
2066               if (PREDICT_FALSE (hop_limit1 <= 0))
2067                 {
2068                   error1 = IP6_ERROR_TIME_EXPIRED;
2069                   next1 = IP6_REWRITE_NEXT_ICMP_ERROR;
2070                   vnet_buffer (p1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
2071                   icmp6_error_set_vnet_buffer (p1, ICMP6_time_exceeded,
2072                                                ICMP6_time_exceeded_ttl_exceeded_in_transit,
2073                                                0);
2074                 }
2075             }
2076           else
2077             {
2078               p1->flags &= ~VNET_BUFFER_LOCALLY_ORIGINATED;
2079             }
2080           adj0 = adj_get (adj_index0);
2081           adj1 = adj_get (adj_index1);
2082
2083           rw_len0 = adj0[0].rewrite_header.data_bytes;
2084           rw_len1 = adj1[0].rewrite_header.data_bytes;
2085           vnet_buffer (p0)->ip.save_rewrite_length = rw_len0;
2086           vnet_buffer (p1)->ip.save_rewrite_length = rw_len1;
2087
2088           if (do_counters)
2089             {
2090               vlib_increment_combined_counter
2091                 (&adjacency_counters,
2092                  thread_index, adj_index0, 1,
2093                  vlib_buffer_length_in_chain (vm, p0) + rw_len0);
2094               vlib_increment_combined_counter
2095                 (&adjacency_counters,
2096                  thread_index, adj_index1, 1,
2097                  vlib_buffer_length_in_chain (vm, p1) + rw_len1);
2098             }
2099
2100           /* Check MTU of outgoing interface. */
2101           error0 =
2102             (vlib_buffer_length_in_chain (vm, p0) >
2103              adj0[0].
2104              rewrite_header.max_l3_packet_bytes ? IP6_ERROR_MTU_EXCEEDED :
2105              error0);
2106           error1 =
2107             (vlib_buffer_length_in_chain (vm, p1) >
2108              adj1[0].
2109              rewrite_header.max_l3_packet_bytes ? IP6_ERROR_MTU_EXCEEDED :
2110              error1);
2111
2112           /* Don't adjust the buffer for hop count issue; icmp-error node
2113            * wants to see the IP headerr */
2114           if (PREDICT_TRUE (error0 == IP6_ERROR_NONE))
2115             {
2116               p0->current_data -= rw_len0;
2117               p0->current_length += rw_len0;
2118
2119               tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index;
2120               vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
2121               next0 = adj0[0].rewrite_header.next_index;
2122
2123               if (PREDICT_FALSE
2124                   (adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
2125                 vnet_feature_arc_start (lm->output_feature_arc_index,
2126                                         tx_sw_if_index0, &next0, p0);
2127             }
2128           if (PREDICT_TRUE (error1 == IP6_ERROR_NONE))
2129             {
2130               p1->current_data -= rw_len1;
2131               p1->current_length += rw_len1;
2132
2133               tx_sw_if_index1 = adj1[0].rewrite_header.sw_if_index;
2134               vnet_buffer (p1)->sw_if_index[VLIB_TX] = tx_sw_if_index1;
2135               next1 = adj1[0].rewrite_header.next_index;
2136
2137               if (PREDICT_FALSE
2138                   (adj1[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
2139                 vnet_feature_arc_start (lm->output_feature_arc_index,
2140                                         tx_sw_if_index1, &next1, p1);
2141             }
2142
2143           /* Guess we are only writing on simple Ethernet header. */
2144           vnet_rewrite_two_headers (adj0[0], adj1[0],
2145                                     ip0, ip1, sizeof (ethernet_header_t));
2146
2147           if (is_midchain)
2148             {
2149               adj0->sub_type.midchain.fixup_func (vm, adj0, p0);
2150               adj1->sub_type.midchain.fixup_func (vm, adj1, p1);
2151             }
2152           if (is_mcast)
2153             {
2154               /*
2155                * copy bytes from the IP address into the MAC rewrite
2156                */
2157               vnet_fixup_one_header (adj0[0], &ip0->dst_address, ip0);
2158               vnet_fixup_one_header (adj1[0], &ip1->dst_address, ip1);
2159             }
2160
2161           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
2162                                            to_next, n_left_to_next,
2163                                            pi0, pi1, next0, next1);
2164         }
2165
2166       while (n_left_from > 0 && n_left_to_next > 0)
2167         {
2168           ip_adjacency_t *adj0;
2169           vlib_buffer_t *p0;
2170           ip6_header_t *ip0;
2171           u32 pi0, rw_len0;
2172           u32 adj_index0, next0, error0;
2173           u32 tx_sw_if_index0;
2174
2175           pi0 = to_next[0] = from[0];
2176
2177           p0 = vlib_get_buffer (vm, pi0);
2178
2179           adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
2180
2181           adj0 = adj_get (adj_index0);
2182
2183           ip0 = vlib_buffer_get_current (p0);
2184
2185           error0 = IP6_ERROR_NONE;
2186           next0 = IP6_REWRITE_NEXT_DROP;
2187
2188           /* Check hop limit */
2189           if (PREDICT_TRUE (!(p0->flags & VNET_BUFFER_LOCALLY_ORIGINATED)))
2190             {
2191               i32 hop_limit0 = ip0->hop_limit;
2192
2193               ASSERT (ip0->hop_limit > 0);
2194
2195               hop_limit0 -= 1;
2196
2197               ip0->hop_limit = hop_limit0;
2198
2199               if (PREDICT_FALSE (hop_limit0 <= 0))
2200                 {
2201                   /*
2202                    * If the hop count drops below 1 when forwarding, generate
2203                    * an ICMP response.
2204                    */
2205                   error0 = IP6_ERROR_TIME_EXPIRED;
2206                   next0 = IP6_REWRITE_NEXT_ICMP_ERROR;
2207                   vnet_buffer (p0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
2208                   icmp6_error_set_vnet_buffer (p0, ICMP6_time_exceeded,
2209                                                ICMP6_time_exceeded_ttl_exceeded_in_transit,
2210                                                0);
2211                 }
2212             }
2213           else
2214             {
2215               p0->flags &= ~VNET_BUFFER_LOCALLY_ORIGINATED;
2216             }
2217
2218           /* Guess we are only writing on simple Ethernet header. */
2219           vnet_rewrite_one_header (adj0[0], ip0, sizeof (ethernet_header_t));
2220
2221           /* Update packet buffer attributes/set output interface. */
2222           rw_len0 = adj0[0].rewrite_header.data_bytes;
2223           vnet_buffer (p0)->ip.save_rewrite_length = rw_len0;
2224
2225           if (do_counters)
2226             {
2227               vlib_increment_combined_counter
2228                 (&adjacency_counters,
2229                  thread_index, adj_index0, 1,
2230                  vlib_buffer_length_in_chain (vm, p0) + rw_len0);
2231             }
2232
2233           /* Check MTU of outgoing interface. */
2234           error0 =
2235             (vlib_buffer_length_in_chain (vm, p0) >
2236              adj0[0].
2237              rewrite_header.max_l3_packet_bytes ? IP6_ERROR_MTU_EXCEEDED :
2238              error0);
2239
2240           /* Don't adjust the buffer for hop count issue; icmp-error node
2241            * wants to see the IP headerr */
2242           if (PREDICT_TRUE (error0 == IP6_ERROR_NONE))
2243             {
2244               p0->current_data -= rw_len0;
2245               p0->current_length += rw_len0;
2246
2247               tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index;
2248
2249               vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
2250               next0 = adj0[0].rewrite_header.next_index;
2251
2252               if (PREDICT_FALSE
2253                   (adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
2254                 vnet_feature_arc_start (lm->output_feature_arc_index,
2255                                         tx_sw_if_index0, &next0, p0);
2256             }
2257
2258           if (is_midchain)
2259             {
2260               adj0->sub_type.midchain.fixup_func (vm, adj0, p0);
2261             }
2262           if (is_mcast)
2263             {
2264               vnet_fixup_one_header (adj0[0], &ip0->dst_address, ip0);
2265             }
2266
2267           p0->error = error_node->errors[error0];
2268
2269           from += 1;
2270           n_left_from -= 1;
2271           to_next += 1;
2272           n_left_to_next -= 1;
2273
2274           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2275                                            to_next, n_left_to_next,
2276                                            pi0, next0);
2277         }
2278
2279       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2280     }
2281
2282   /* Need to do trace after rewrites to pick up new packet data. */
2283   if (node->flags & VLIB_NODE_FLAG_TRACE)
2284     ip6_forward_next_trace (vm, node, frame, VLIB_TX);
2285
2286   return frame->n_vectors;
2287 }
2288
2289 static uword
2290 ip6_rewrite (vlib_main_t * vm,
2291              vlib_node_runtime_t * node, vlib_frame_t * frame)
2292 {
2293   if (adj_are_counters_enabled ())
2294     return ip6_rewrite_inline (vm, node, frame, 1, 0, 0);
2295   else
2296     return ip6_rewrite_inline (vm, node, frame, 0, 0, 0);
2297 }
2298
2299 static uword
2300 ip6_rewrite_mcast (vlib_main_t * vm,
2301                    vlib_node_runtime_t * node, vlib_frame_t * frame)
2302 {
2303   if (adj_are_counters_enabled ())
2304     return ip6_rewrite_inline (vm, node, frame, 1, 0, 1);
2305   else
2306     return ip6_rewrite_inline (vm, node, frame, 0, 0, 1);
2307 }
2308
2309 static uword
2310 ip6_midchain (vlib_main_t * vm,
2311               vlib_node_runtime_t * node, vlib_frame_t * frame)
2312 {
2313   if (adj_are_counters_enabled ())
2314     return ip6_rewrite_inline (vm, node, frame, 1, 1, 0);
2315   else
2316     return ip6_rewrite_inline (vm, node, frame, 0, 1, 0);
2317 }
2318
2319 static uword
2320 ip6_mcast_midchain (vlib_main_t * vm,
2321                     vlib_node_runtime_t * node, vlib_frame_t * frame)
2322 {
2323   if (adj_are_counters_enabled ())
2324     return ip6_rewrite_inline (vm, node, frame, 1, 1, 1);
2325   else
2326     return ip6_rewrite_inline (vm, node, frame, 0, 1, 1);
2327 }
2328
2329 /* *INDENT-OFF* */
2330 VLIB_REGISTER_NODE (ip6_midchain_node) =
2331 {
2332   .function = ip6_midchain,
2333   .name = "ip6-midchain",
2334   .vector_size = sizeof (u32),
2335   .format_trace = format_ip6_forward_next_trace,
2336   .sibling_of = "ip6-rewrite",
2337   };
2338 /* *INDENT-ON* */
2339
2340 VLIB_NODE_FUNCTION_MULTIARCH (ip6_midchain_node, ip6_midchain);
2341
2342 /* *INDENT-OFF* */
2343 VLIB_REGISTER_NODE (ip6_rewrite_node) =
2344 {
2345   .function = ip6_rewrite,
2346   .name = "ip6-rewrite",
2347   .vector_size = sizeof (u32),
2348   .format_trace = format_ip6_rewrite_trace,
2349   .n_next_nodes = 2,
2350   .next_nodes =
2351   {
2352     [IP6_REWRITE_NEXT_DROP] = "error-drop",
2353     [IP6_REWRITE_NEXT_ICMP_ERROR] = "ip6-icmp-error",
2354   },
2355 };
2356 /* *INDENT-ON* */
2357
2358 VLIB_NODE_FUNCTION_MULTIARCH (ip6_rewrite_node, ip6_rewrite);
2359
2360 /* *INDENT-OFF* */
2361 VLIB_REGISTER_NODE (ip6_rewrite_mcast_node) =
2362 {
2363   .function = ip6_rewrite_mcast,
2364   .name = "ip6-rewrite-mcast",
2365   .vector_size = sizeof (u32),
2366   .format_trace = format_ip6_rewrite_trace,
2367   .sibling_of = "ip6-rewrite",
2368 };
2369 /* *INDENT-ON* */
2370
2371 VLIB_NODE_FUNCTION_MULTIARCH (ip6_rewrite_mcast_node, ip6_rewrite_mcast);
2372
2373 /* *INDENT-OFF* */
2374 VLIB_REGISTER_NODE (ip6_mcast_midchain_node, static) =
2375 {
2376   .function = ip6_mcast_midchain,
2377   .name = "ip6-mcast-midchain",
2378   .vector_size = sizeof (u32),
2379   .format_trace = format_ip6_rewrite_trace,
2380   .sibling_of = "ip6-rewrite",
2381 };
2382 /* *INDENT-ON* */
2383
2384 VLIB_NODE_FUNCTION_MULTIARCH (ip6_mcast_midchain_node, ip6_mcast_midchain);
2385
2386 /*
2387  * Hop-by-Hop handling
2388  */
2389 ip6_hop_by_hop_main_t ip6_hop_by_hop_main;
2390
2391 #define foreach_ip6_hop_by_hop_error \
2392 _(PROCESSED, "pkts with ip6 hop-by-hop options") \
2393 _(FORMAT, "incorrectly formatted hop-by-hop options") \
2394 _(UNKNOWN_OPTION, "unknown ip6 hop-by-hop options")
2395
2396 /* *INDENT-OFF* */
2397 typedef enum
2398 {
2399 #define _(sym,str) IP6_HOP_BY_HOP_ERROR_##sym,
2400   foreach_ip6_hop_by_hop_error
2401 #undef _
2402   IP6_HOP_BY_HOP_N_ERROR,
2403 } ip6_hop_by_hop_error_t;
2404 /* *INDENT-ON* */
2405
2406 /*
2407  * Primary h-b-h handler trace support
2408  * We work pretty hard on the problem for obvious reasons
2409  */
2410 typedef struct
2411 {
2412   u32 next_index;
2413   u32 trace_len;
2414   u8 option_data[256];
2415 } ip6_hop_by_hop_trace_t;
2416
2417 vlib_node_registration_t ip6_hop_by_hop_node;
2418
2419 static char *ip6_hop_by_hop_error_strings[] = {
2420 #define _(sym,string) string,
2421   foreach_ip6_hop_by_hop_error
2422 #undef _
2423 };
2424
2425 u8 *
2426 format_ip6_hop_by_hop_ext_hdr (u8 * s, va_list * args)
2427 {
2428   ip6_hop_by_hop_header_t *hbh0 = va_arg (*args, ip6_hop_by_hop_header_t *);
2429   int total_len = va_arg (*args, int);
2430   ip6_hop_by_hop_option_t *opt0, *limit0;
2431   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2432   u8 type0;
2433
2434   s = format (s, "IP6_HOP_BY_HOP: next protocol %d len %d total %d",
2435               hbh0->protocol, (hbh0->length + 1) << 3, total_len);
2436
2437   opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
2438   limit0 = (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 + total_len);
2439
2440   while (opt0 < limit0)
2441     {
2442       type0 = opt0->type;
2443       switch (type0)
2444         {
2445         case 0:         /* Pad, just stop */
2446           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0 + 1);
2447           break;
2448
2449         default:
2450           if (hm->trace[type0])
2451             {
2452               s = (*hm->trace[type0]) (s, opt0);
2453             }
2454           else
2455             {
2456               s =
2457                 format (s, "\n    unrecognized option %d length %d", type0,
2458                         opt0->length);
2459             }
2460           opt0 =
2461             (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
2462                                          sizeof (ip6_hop_by_hop_option_t));
2463           break;
2464         }
2465     }
2466   return s;
2467 }
2468
2469 static u8 *
2470 format_ip6_hop_by_hop_trace (u8 * s, va_list * args)
2471 {
2472   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
2473   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
2474   ip6_hop_by_hop_trace_t *t = va_arg (*args, ip6_hop_by_hop_trace_t *);
2475   ip6_hop_by_hop_header_t *hbh0;
2476   ip6_hop_by_hop_option_t *opt0, *limit0;
2477   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2478
2479   u8 type0;
2480
2481   hbh0 = (ip6_hop_by_hop_header_t *) t->option_data;
2482
2483   s = format (s, "IP6_HOP_BY_HOP: next index %d len %d traced %d",
2484               t->next_index, (hbh0->length + 1) << 3, t->trace_len);
2485
2486   opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
2487   limit0 = (ip6_hop_by_hop_option_t *) ((u8 *) hbh0) + t->trace_len;
2488
2489   while (opt0 < limit0)
2490     {
2491       type0 = opt0->type;
2492       switch (type0)
2493         {
2494         case 0:         /* Pad, just stop */
2495           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
2496           break;
2497
2498         default:
2499           if (hm->trace[type0])
2500             {
2501               s = (*hm->trace[type0]) (s, opt0);
2502             }
2503           else
2504             {
2505               s =
2506                 format (s, "\n    unrecognized option %d length %d", type0,
2507                         opt0->length);
2508             }
2509           opt0 =
2510             (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
2511                                          sizeof (ip6_hop_by_hop_option_t));
2512           break;
2513         }
2514     }
2515   return s;
2516 }
2517
2518 always_inline u8
2519 ip6_scan_hbh_options (vlib_buffer_t * b0,
2520                       ip6_header_t * ip0,
2521                       ip6_hop_by_hop_header_t * hbh0,
2522                       ip6_hop_by_hop_option_t * opt0,
2523                       ip6_hop_by_hop_option_t * limit0, u32 * next0)
2524 {
2525   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2526   u8 type0;
2527   u8 error0 = 0;
2528
2529   while (opt0 < limit0)
2530     {
2531       type0 = opt0->type;
2532       switch (type0)
2533         {
2534         case 0:         /* Pad1 */
2535           opt0 = (ip6_hop_by_hop_option_t *) ((u8 *) opt0) + 1;
2536           continue;
2537         case 1:         /* PadN */
2538           break;
2539         default:
2540           if (hm->options[type0])
2541             {
2542               if ((*hm->options[type0]) (b0, ip0, opt0) < 0)
2543                 {
2544                   error0 = IP6_HOP_BY_HOP_ERROR_FORMAT;
2545                   return (error0);
2546                 }
2547             }
2548           else
2549             {
2550               /* Unrecognized mandatory option, check the two high order bits */
2551               switch (opt0->type & HBH_OPTION_TYPE_HIGH_ORDER_BITS)
2552                 {
2553                 case HBH_OPTION_TYPE_SKIP_UNKNOWN:
2554                   break;
2555                 case HBH_OPTION_TYPE_DISCARD_UNKNOWN:
2556                   error0 = IP6_HOP_BY_HOP_ERROR_UNKNOWN_OPTION;
2557                   *next0 = IP_LOOKUP_NEXT_DROP;
2558                   break;
2559                 case HBH_OPTION_TYPE_DISCARD_UNKNOWN_ICMP:
2560                   error0 = IP6_HOP_BY_HOP_ERROR_UNKNOWN_OPTION;
2561                   *next0 = IP_LOOKUP_NEXT_ICMP_ERROR;
2562                   icmp6_error_set_vnet_buffer (b0, ICMP6_parameter_problem,
2563                                                ICMP6_parameter_problem_unrecognized_option,
2564                                                (u8 *) opt0 - (u8 *) ip0);
2565                   break;
2566                 case HBH_OPTION_TYPE_DISCARD_UNKNOWN_ICMP_NOT_MCAST:
2567                   error0 = IP6_HOP_BY_HOP_ERROR_UNKNOWN_OPTION;
2568                   if (!ip6_address_is_multicast (&ip0->dst_address))
2569                     {
2570                       *next0 = IP_LOOKUP_NEXT_ICMP_ERROR;
2571                       icmp6_error_set_vnet_buffer (b0,
2572                                                    ICMP6_parameter_problem,
2573                                                    ICMP6_parameter_problem_unrecognized_option,
2574                                                    (u8 *) opt0 - (u8 *) ip0);
2575                     }
2576                   else
2577                     {
2578                       *next0 = IP_LOOKUP_NEXT_DROP;
2579                     }
2580                   break;
2581                 }
2582               return (error0);
2583             }
2584         }
2585       opt0 =
2586         (ip6_hop_by_hop_option_t *) (((u8 *) opt0) + opt0->length +
2587                                      sizeof (ip6_hop_by_hop_option_t));
2588     }
2589   return (error0);
2590 }
2591
2592 /*
2593  * Process the Hop-by-Hop Options header
2594  */
2595 static uword
2596 ip6_hop_by_hop (vlib_main_t * vm,
2597                 vlib_node_runtime_t * node, vlib_frame_t * frame)
2598 {
2599   vlib_node_runtime_t *error_node =
2600     vlib_node_get_runtime (vm, ip6_hop_by_hop_node.index);
2601   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2602   u32 n_left_from, *from, *to_next;
2603   ip_lookup_next_t next_index;
2604
2605   from = vlib_frame_vector_args (frame);
2606   n_left_from = frame->n_vectors;
2607   next_index = node->cached_next_index;
2608
2609   while (n_left_from > 0)
2610     {
2611       u32 n_left_to_next;
2612
2613       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2614
2615       while (n_left_from >= 4 && n_left_to_next >= 2)
2616         {
2617           u32 bi0, bi1;
2618           vlib_buffer_t *b0, *b1;
2619           u32 next0, next1;
2620           ip6_header_t *ip0, *ip1;
2621           ip6_hop_by_hop_header_t *hbh0, *hbh1;
2622           ip6_hop_by_hop_option_t *opt0, *limit0, *opt1, *limit1;
2623           u8 error0 = 0, error1 = 0;
2624
2625           /* Prefetch next iteration. */
2626           {
2627             vlib_buffer_t *p2, *p3;
2628
2629             p2 = vlib_get_buffer (vm, from[2]);
2630             p3 = vlib_get_buffer (vm, from[3]);
2631
2632             vlib_prefetch_buffer_header (p2, LOAD);
2633             vlib_prefetch_buffer_header (p3, LOAD);
2634
2635             CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2636             CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
2637           }
2638
2639           /* Speculatively enqueue b0, b1 to the current next frame */
2640           to_next[0] = bi0 = from[0];
2641           to_next[1] = bi1 = from[1];
2642           from += 2;
2643           to_next += 2;
2644           n_left_from -= 2;
2645           n_left_to_next -= 2;
2646
2647           b0 = vlib_get_buffer (vm, bi0);
2648           b1 = vlib_get_buffer (vm, bi1);
2649
2650           /* Default use the next_index from the adjacency. A HBH option rarely redirects to a different node */
2651           u32 adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
2652           ip_adjacency_t *adj0 = adj_get (adj_index0);
2653           u32 adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
2654           ip_adjacency_t *adj1 = adj_get (adj_index1);
2655
2656           /* Default use the next_index from the adjacency. A HBH option rarely redirects to a different node */
2657           next0 = adj0->lookup_next_index;
2658           next1 = adj1->lookup_next_index;
2659
2660           ip0 = vlib_buffer_get_current (b0);
2661           ip1 = vlib_buffer_get_current (b1);
2662           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
2663           hbh1 = (ip6_hop_by_hop_header_t *) (ip1 + 1);
2664           opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
2665           opt1 = (ip6_hop_by_hop_option_t *) (hbh1 + 1);
2666           limit0 =
2667             (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 +
2668                                          ((hbh0->length + 1) << 3));
2669           limit1 =
2670             (ip6_hop_by_hop_option_t *) ((u8 *) hbh1 +
2671                                          ((hbh1->length + 1) << 3));
2672
2673           /*
2674            * Basic validity checks
2675            */
2676           if ((hbh0->length + 1) << 3 >
2677               clib_net_to_host_u16 (ip0->payload_length))
2678             {
2679               error0 = IP6_HOP_BY_HOP_ERROR_FORMAT;
2680               next0 = IP_LOOKUP_NEXT_DROP;
2681               goto outdual;
2682             }
2683           /* Scan the set of h-b-h options, process ones that we understand */
2684           error0 = ip6_scan_hbh_options (b0, ip0, hbh0, opt0, limit0, &next0);
2685
2686           if ((hbh1->length + 1) << 3 >
2687               clib_net_to_host_u16 (ip1->payload_length))
2688             {
2689               error1 = IP6_HOP_BY_HOP_ERROR_FORMAT;
2690               next1 = IP_LOOKUP_NEXT_DROP;
2691               goto outdual;
2692             }
2693           /* Scan the set of h-b-h options, process ones that we understand */
2694           error1 = ip6_scan_hbh_options (b1, ip1, hbh1, opt1, limit1, &next1);
2695
2696         outdual:
2697           /* Has the classifier flagged this buffer for special treatment? */
2698           if (PREDICT_FALSE
2699               ((error0 == 0)
2700                && (vnet_buffer (b0)->l2_classify.opaque_index & OI_DECAP)))
2701             next0 = hm->next_override;
2702
2703           /* Has the classifier flagged this buffer for special treatment? */
2704           if (PREDICT_FALSE
2705               ((error1 == 0)
2706                && (vnet_buffer (b1)->l2_classify.opaque_index & OI_DECAP)))
2707             next1 = hm->next_override;
2708
2709           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
2710             {
2711               if (b0->flags & VLIB_BUFFER_IS_TRACED)
2712                 {
2713                   ip6_hop_by_hop_trace_t *t =
2714                     vlib_add_trace (vm, node, b0, sizeof (*t));
2715                   u32 trace_len = (hbh0->length + 1) << 3;
2716                   t->next_index = next0;
2717                   /* Capture the h-b-h option verbatim */
2718                   trace_len =
2719                     trace_len <
2720                     ARRAY_LEN (t->option_data) ? trace_len :
2721                     ARRAY_LEN (t->option_data);
2722                   t->trace_len = trace_len;
2723                   clib_memcpy (t->option_data, hbh0, trace_len);
2724                 }
2725               if (b1->flags & VLIB_BUFFER_IS_TRACED)
2726                 {
2727                   ip6_hop_by_hop_trace_t *t =
2728                     vlib_add_trace (vm, node, b1, sizeof (*t));
2729                   u32 trace_len = (hbh1->length + 1) << 3;
2730                   t->next_index = next1;
2731                   /* Capture the h-b-h option verbatim */
2732                   trace_len =
2733                     trace_len <
2734                     ARRAY_LEN (t->option_data) ? trace_len :
2735                     ARRAY_LEN (t->option_data);
2736                   t->trace_len = trace_len;
2737                   clib_memcpy (t->option_data, hbh1, trace_len);
2738                 }
2739
2740             }
2741
2742           b0->error = error_node->errors[error0];
2743           b1->error = error_node->errors[error1];
2744
2745           /* verify speculative enqueue, maybe switch current next frame */
2746           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
2747                                            n_left_to_next, bi0, bi1, next0,
2748                                            next1);
2749         }
2750
2751       while (n_left_from > 0 && n_left_to_next > 0)
2752         {
2753           u32 bi0;
2754           vlib_buffer_t *b0;
2755           u32 next0;
2756           ip6_header_t *ip0;
2757           ip6_hop_by_hop_header_t *hbh0;
2758           ip6_hop_by_hop_option_t *opt0, *limit0;
2759           u8 error0 = 0;
2760
2761           /* Speculatively enqueue b0 to the current next frame */
2762           bi0 = from[0];
2763           to_next[0] = bi0;
2764           from += 1;
2765           to_next += 1;
2766           n_left_from -= 1;
2767           n_left_to_next -= 1;
2768
2769           b0 = vlib_get_buffer (vm, bi0);
2770           /*
2771            * Default use the next_index from the adjacency.
2772            * A HBH option rarely redirects to a different node
2773            */
2774           u32 adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
2775           ip_adjacency_t *adj0 = adj_get (adj_index0);
2776           next0 = adj0->lookup_next_index;
2777
2778           ip0 = vlib_buffer_get_current (b0);
2779           hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
2780           opt0 = (ip6_hop_by_hop_option_t *) (hbh0 + 1);
2781           limit0 =
2782             (ip6_hop_by_hop_option_t *) ((u8 *) hbh0 +
2783                                          ((hbh0->length + 1) << 3));
2784
2785           /*
2786            * Basic validity checks
2787            */
2788           if ((hbh0->length + 1) << 3 >
2789               clib_net_to_host_u16 (ip0->payload_length))
2790             {
2791               error0 = IP6_HOP_BY_HOP_ERROR_FORMAT;
2792               next0 = IP_LOOKUP_NEXT_DROP;
2793               goto out0;
2794             }
2795
2796           /* Scan the set of h-b-h options, process ones that we understand */
2797           error0 = ip6_scan_hbh_options (b0, ip0, hbh0, opt0, limit0, &next0);
2798
2799         out0:
2800           /* Has the classifier flagged this buffer for special treatment? */
2801           if (PREDICT_FALSE
2802               ((error0 == 0)
2803                && (vnet_buffer (b0)->l2_classify.opaque_index & OI_DECAP)))
2804             next0 = hm->next_override;
2805
2806           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
2807             {
2808               ip6_hop_by_hop_trace_t *t =
2809                 vlib_add_trace (vm, node, b0, sizeof (*t));
2810               u32 trace_len = (hbh0->length + 1) << 3;
2811               t->next_index = next0;
2812               /* Capture the h-b-h option verbatim */
2813               trace_len =
2814                 trace_len <
2815                 ARRAY_LEN (t->option_data) ? trace_len :
2816                 ARRAY_LEN (t->option_data);
2817               t->trace_len = trace_len;
2818               clib_memcpy (t->option_data, hbh0, trace_len);
2819             }
2820
2821           b0->error = error_node->errors[error0];
2822
2823           /* verify speculative enqueue, maybe switch current next frame */
2824           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
2825                                            n_left_to_next, bi0, next0);
2826         }
2827       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2828     }
2829   return frame->n_vectors;
2830 }
2831
2832 /* *INDENT-OFF* */
2833 VLIB_REGISTER_NODE (ip6_hop_by_hop_node) =
2834 {
2835   .function = ip6_hop_by_hop,
2836   .name = "ip6-hop-by-hop",
2837   .sibling_of = "ip6-lookup",
2838   .vector_size = sizeof (u32),
2839   .format_trace = format_ip6_hop_by_hop_trace,
2840   .type = VLIB_NODE_TYPE_INTERNAL,
2841   .n_errors = ARRAY_LEN (ip6_hop_by_hop_error_strings),
2842   .error_strings = ip6_hop_by_hop_error_strings,
2843   .n_next_nodes = 0,
2844 };
2845 /* *INDENT-ON* */
2846
2847 VLIB_NODE_FUNCTION_MULTIARCH (ip6_hop_by_hop_node, ip6_hop_by_hop);
2848
2849 static clib_error_t *
2850 ip6_hop_by_hop_init (vlib_main_t * vm)
2851 {
2852   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2853   memset (hm->options, 0, sizeof (hm->options));
2854   memset (hm->trace, 0, sizeof (hm->trace));
2855   hm->next_override = IP6_LOOKUP_NEXT_POP_HOP_BY_HOP;
2856   return (0);
2857 }
2858
2859 VLIB_INIT_FUNCTION (ip6_hop_by_hop_init);
2860
2861 void
2862 ip6_hbh_set_next_override (uword next)
2863 {
2864   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2865
2866   hm->next_override = next;
2867 }
2868
2869 int
2870 ip6_hbh_register_option (u8 option,
2871                          int options (vlib_buffer_t * b, ip6_header_t * ip,
2872                                       ip6_hop_by_hop_option_t * opt),
2873                          u8 * trace (u8 * s, ip6_hop_by_hop_option_t * opt))
2874 {
2875   ip6_main_t *im = &ip6_main;
2876   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2877
2878   ASSERT (option < ARRAY_LEN (hm->options));
2879
2880   /* Already registered */
2881   if (hm->options[option])
2882     return (-1);
2883
2884   hm->options[option] = options;
2885   hm->trace[option] = trace;
2886
2887   /* Set global variable */
2888   im->hbh_enabled = 1;
2889
2890   return (0);
2891 }
2892
2893 int
2894 ip6_hbh_unregister_option (u8 option)
2895 {
2896   ip6_main_t *im = &ip6_main;
2897   ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main;
2898
2899   ASSERT (option < ARRAY_LEN (hm->options));
2900
2901   /* Not registered */
2902   if (!hm->options[option])
2903     return (-1);
2904
2905   hm->options[option] = NULL;
2906   hm->trace[option] = NULL;
2907
2908   /* Disable global knob if this was the last option configured */
2909   int i;
2910   bool found = false;
2911   for (i = 0; i < 256; i++)
2912     {
2913       if (hm->options[option])
2914         {
2915           found = true;
2916           break;
2917         }
2918     }
2919   if (!found)
2920     im->hbh_enabled = 0;
2921
2922   return (0);
2923 }
2924
2925 /* Global IP6 main. */
2926 ip6_main_t ip6_main;
2927
2928 static clib_error_t *
2929 ip6_lookup_init (vlib_main_t * vm)
2930 {
2931   ip6_main_t *im = &ip6_main;
2932   clib_error_t *error;
2933   uword i;
2934
2935   if ((error = vlib_call_init_function (vm, vnet_feature_init)))
2936     return error;
2937
2938   for (i = 0; i < ARRAY_LEN (im->fib_masks); i++)
2939     {
2940       u32 j, i0, i1;
2941
2942       i0 = i / 32;
2943       i1 = i % 32;
2944
2945       for (j = 0; j < i0; j++)
2946         im->fib_masks[i].as_u32[j] = ~0;
2947
2948       if (i1)
2949         im->fib_masks[i].as_u32[i0] =
2950           clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1));
2951     }
2952
2953   ip_lookup_init (&im->lookup_main, /* is_ip6 */ 1);
2954
2955   if (im->lookup_table_nbuckets == 0)
2956     im->lookup_table_nbuckets = IP6_FIB_DEFAULT_HASH_NUM_BUCKETS;
2957
2958   im->lookup_table_nbuckets = 1 << max_log2 (im->lookup_table_nbuckets);
2959
2960   if (im->lookup_table_size == 0)
2961     im->lookup_table_size = IP6_FIB_DEFAULT_HASH_MEMORY_SIZE;
2962
2963   BV (clib_bihash_init) (&(im->ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash),
2964                          "ip6 FIB fwding table",
2965                          im->lookup_table_nbuckets, im->lookup_table_size);
2966   BV (clib_bihash_init) (&im->ip6_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
2967                          "ip6 FIB non-fwding table",
2968                          im->lookup_table_nbuckets, im->lookup_table_size);
2969
2970   /* Create FIB with index 0 and table id of 0. */
2971   fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0);
2972   mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0);
2973
2974   {
2975     pg_node_t *pn;
2976     pn = pg_get_node (ip6_lookup_node.index);
2977     pn->unformat_edit = unformat_pg_ip6_header;
2978   }
2979
2980   /* Unless explicitly configured, don't process HBH options */
2981   im->hbh_enabled = 0;
2982
2983   {
2984     icmp6_neighbor_solicitation_header_t p;
2985
2986     memset (&p, 0, sizeof (p));
2987
2988     p.ip.ip_version_traffic_class_and_flow_label =
2989       clib_host_to_net_u32 (0x6 << 28);
2990     p.ip.payload_length =
2991       clib_host_to_net_u16 (sizeof (p) -
2992                             STRUCT_OFFSET_OF
2993                             (icmp6_neighbor_solicitation_header_t, neighbor));
2994     p.ip.protocol = IP_PROTOCOL_ICMP6;
2995     p.ip.hop_limit = 255;
2996     ip6_set_solicited_node_multicast_address (&p.ip.dst_address, 0);
2997
2998     p.neighbor.icmp.type = ICMP6_neighbor_solicitation;
2999
3000     p.link_layer_option.header.type =
3001       ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address;
3002     p.link_layer_option.header.n_data_u64s =
3003       sizeof (p.link_layer_option) / sizeof (u64);
3004
3005     vlib_packet_template_init (vm,
3006                                &im->discover_neighbor_packet_template,
3007                                &p, sizeof (p),
3008                                /* alloc chunk size */ 8,
3009                                "ip6 neighbor discovery");
3010   }
3011
3012   return error;
3013 }
3014
3015 VLIB_INIT_FUNCTION (ip6_lookup_init);
3016
3017 static clib_error_t *
3018 add_del_ip6_interface_table (vlib_main_t * vm,
3019                              unformat_input_t * input,
3020                              vlib_cli_command_t * cmd)
3021 {
3022   vnet_main_t *vnm = vnet_get_main ();
3023   ip_interface_address_t *ia;
3024   clib_error_t *error = 0;
3025   u32 sw_if_index, table_id;
3026
3027   sw_if_index = ~0;
3028
3029   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
3030     {
3031       error = clib_error_return (0, "unknown interface `%U'",
3032                                  format_unformat_error, input);
3033       goto done;
3034     }
3035
3036   if (unformat (input, "%d", &table_id))
3037     ;
3038   else
3039     {
3040       error = clib_error_return (0, "expected table id `%U'",
3041                                  format_unformat_error, input);
3042       goto done;
3043     }
3044
3045   /*
3046    * If the interface already has in IP address, then a change int
3047    * VRF is not allowed. The IP address applied must first be removed.
3048    * We do not do that automatically here, since VPP has no knowledge
3049    * of whether thoses subnets are valid in the destination VRF.
3050    */
3051   /* *INDENT-OFF* */
3052   foreach_ip_interface_address (&ip6_main.lookup_main,
3053                                 ia, sw_if_index,
3054                                 1 /* honor unnumbered */,
3055   ({
3056       ip4_address_t * a;
3057
3058       a = ip_interface_address_get_address (&ip6_main.lookup_main, ia);
3059       error = clib_error_return (0, "interface %U has address %U",
3060                                  format_vnet_sw_if_index_name, vnm,
3061                                  sw_if_index,
3062                                  format_ip6_address, a);
3063       goto done;
3064   }));
3065   /* *INDENT-ON* */
3066
3067   {
3068     u32 fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
3069                                                        table_id);
3070
3071     vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index);
3072     ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index;
3073
3074     fib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
3075                                                     table_id);
3076
3077     vec_validate (ip6_main.mfib_index_by_sw_if_index, sw_if_index);
3078     ip6_main.mfib_index_by_sw_if_index[sw_if_index] = fib_index;
3079   }
3080
3081
3082 done:
3083   return error;
3084 }
3085
3086 /*?
3087  * Place the indicated interface into the supplied IPv6 FIB table (also known
3088  * as a VRF). If the FIB table does not exist, this command creates it. To
3089  * display the current IPv6 FIB table, use the command '<em>show ip6 fib</em>'.
3090  * FIB table will only be displayed if a route has been added to the table, or
3091  * an IP Address is assigned to an interface in the table (which adds a route
3092  * automatically).
3093  *
3094  * @note IP addresses added after setting the interface IP table are added to
3095  * the indicated FIB table. If an IP address is added prior to changing the
3096  * table then this is an error. The control plane must remove these addresses
3097  * first and then change the table. VPP will not automatically move the
3098  * addresses from the old to the new table as it does not know the validity
3099  * of such a change.
3100  *
3101  * @cliexpar
3102  * Example of how to add an interface to an IPv6 FIB table (where 2 is the table-id):
3103  * @cliexcmd{set interface ip6 table GigabitEthernet2/0/0 2}
3104  ?*/
3105 /* *INDENT-OFF* */
3106 VLIB_CLI_COMMAND (set_interface_ip6_table_command, static) =
3107 {
3108   .path = "set interface ip6 table",
3109   .function = add_del_ip6_interface_table,
3110   .short_help = "set interface ip6 table <interface> <table-id>"
3111 };
3112 /* *INDENT-ON* */
3113
3114 void
3115 ip6_link_local_address_from_ethernet_mac_address (ip6_address_t * ip,
3116                                                   u8 * mac)
3117 {
3118   ip->as_u64[0] = clib_host_to_net_u64 (0xFE80000000000000ULL);
3119   /* Invert the "u" bit */
3120   ip->as_u8[8] = mac[0] ^ (1 << 1);
3121   ip->as_u8[9] = mac[1];
3122   ip->as_u8[10] = mac[2];
3123   ip->as_u8[11] = 0xFF;
3124   ip->as_u8[12] = 0xFE;
3125   ip->as_u8[13] = mac[3];
3126   ip->as_u8[14] = mac[4];
3127   ip->as_u8[15] = mac[5];
3128 }
3129
3130 void
3131 ip6_ethernet_mac_address_from_link_local_address (u8 * mac,
3132                                                   ip6_address_t * ip)
3133 {
3134   /* Invert the previously inverted "u" bit */
3135   mac[0] = ip->as_u8[8] ^ (1 << 1);
3136   mac[1] = ip->as_u8[9];
3137   mac[2] = ip->as_u8[10];
3138   mac[3] = ip->as_u8[13];
3139   mac[4] = ip->as_u8[14];
3140   mac[5] = ip->as_u8[15];
3141 }
3142
3143 static clib_error_t *
3144 test_ip6_link_command_fn (vlib_main_t * vm,
3145                           unformat_input_t * input, vlib_cli_command_t * cmd)
3146 {
3147   u8 mac[6];
3148   ip6_address_t _a, *a = &_a;
3149
3150   if (unformat (input, "%U", unformat_ethernet_address, mac))
3151     {
3152       ip6_link_local_address_from_ethernet_mac_address (a, mac);
3153       vlib_cli_output (vm, "Link local address: %U", format_ip6_address, a);
3154       ip6_ethernet_mac_address_from_link_local_address (mac, a);
3155       vlib_cli_output (vm, "Original MAC address: %U",
3156                        format_ethernet_address, mac);
3157     }
3158
3159   return 0;
3160 }
3161
3162 /*?
3163  * This command converts the given MAC Address into an IPv6 link-local
3164  * address.
3165  *
3166  * @cliexpar
3167  * Example of how to create an IPv6 link-local address:
3168  * @cliexstart{test ip6 link 16:d9:e0:91:79:86}
3169  * Link local address: fe80::14d9:e0ff:fe91:7986
3170  * Original MAC address: 16:d9:e0:91:79:86
3171  * @cliexend
3172 ?*/
3173 /* *INDENT-OFF* */
3174 VLIB_CLI_COMMAND (test_link_command, static) =
3175 {
3176   .path = "test ip6 link",
3177   .function = test_ip6_link_command_fn,
3178   .short_help = "test ip6 link <mac-address>",
3179 };
3180 /* *INDENT-ON* */
3181
3182 int
3183 vnet_set_ip6_flow_hash (u32 table_id, u32 flow_hash_config)
3184 {
3185   u32 fib_index;
3186
3187   fib_index = fib_table_find (FIB_PROTOCOL_IP6, table_id);
3188
3189   if (~0 == fib_index)
3190     return VNET_API_ERROR_NO_SUCH_FIB;
3191
3192   fib_table_set_flow_hash_config (fib_index, FIB_PROTOCOL_IP6,
3193                                   flow_hash_config);
3194
3195   return 0;
3196 }
3197
3198 static clib_error_t *
3199 set_ip6_flow_hash_command_fn (vlib_main_t * vm,
3200                               unformat_input_t * input,
3201                               vlib_cli_command_t * cmd)
3202 {
3203   int matched = 0;
3204   u32 table_id = 0;
3205   u32 flow_hash_config = 0;
3206   int rv;
3207
3208   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3209     {
3210       if (unformat (input, "table %d", &table_id))
3211         matched = 1;
3212 #define _(a,v) \
3213     else if (unformat (input, #a)) { flow_hash_config |= v; matched=1;}
3214       foreach_flow_hash_bit
3215 #undef _
3216         else
3217         break;
3218     }
3219
3220   if (matched == 0)
3221     return clib_error_return (0, "unknown input `%U'",
3222                               format_unformat_error, input);
3223
3224   rv = vnet_set_ip6_flow_hash (table_id, flow_hash_config);
3225   switch (rv)
3226     {
3227     case 0:
3228       break;
3229
3230     case -1:
3231       return clib_error_return (0, "no such FIB table %d", table_id);
3232
3233     default:
3234       clib_warning ("BUG: illegal flow hash config 0x%x", flow_hash_config);
3235       break;
3236     }
3237
3238   return 0;
3239 }
3240
3241 /*?
3242  * Configure the set of IPv6 fields used by the flow hash.
3243  *
3244  * @cliexpar
3245  * @parblock
3246  * Example of how to set the flow hash on a given table:
3247  * @cliexcmd{set ip6 flow-hash table 8 dst sport dport proto}
3248  *
3249  * Example of display the configured flow hash:
3250  * @cliexstart{show ip6 fib}
3251  * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
3252  * @::/0
3253  *   unicast-ip6-chain
3254  *   [@0]: dpo-load-balance: [index:5 buckets:1 uRPF:5 to:[0:0]]
3255  *     [0] [@0]: dpo-drop ip6
3256  * fe80::/10
3257  *   unicast-ip6-chain
3258  *   [@0]: dpo-load-balance: [index:10 buckets:1 uRPF:10 to:[0:0]]
3259  *     [0] [@2]: dpo-receive
3260  * ff02::1/128
3261  *   unicast-ip6-chain
3262  *   [@0]: dpo-load-balance: [index:8 buckets:1 uRPF:8 to:[0:0]]
3263  *     [0] [@2]: dpo-receive
3264  * ff02::2/128
3265  *   unicast-ip6-chain
3266  *   [@0]: dpo-load-balance: [index:7 buckets:1 uRPF:7 to:[0:0]]
3267  *     [0] [@2]: dpo-receive
3268  * ff02::16/128
3269  *   unicast-ip6-chain
3270  *   [@0]: dpo-load-balance: [index:9 buckets:1 uRPF:9 to:[0:0]]
3271  *     [0] [@2]: dpo-receive
3272  * ff02::1:ff00:0/104
3273  *   unicast-ip6-chain
3274  *   [@0]: dpo-load-balance: [index:6 buckets:1 uRPF:6 to:[0:0]]
3275  *     [0] [@2]: dpo-receive
3276  * ipv6-VRF:8, fib_index 1, flow hash: dst sport dport proto
3277  * @::/0
3278  *   unicast-ip6-chain
3279  *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
3280  *     [0] [@0]: dpo-drop ip6
3281  * @::a:1:1:0:4/126
3282  *   unicast-ip6-chain
3283  *   [@0]: dpo-load-balance: [index:27 buckets:1 uRPF:26 to:[0:0]]
3284  *     [0] [@4]: ipv6-glean: af_packet0
3285  * @::a:1:1:0:7/128
3286  *   unicast-ip6-chain
3287  *   [@0]: dpo-load-balance: [index:28 buckets:1 uRPF:27 to:[0:0]]
3288  *     [0] [@2]: dpo-receive: @::a:1:1:0:7 on af_packet0
3289  * fe80::/10
3290  *   unicast-ip6-chain
3291  *   [@0]: dpo-load-balance: [index:26 buckets:1 uRPF:25 to:[0:0]]
3292  *     [0] [@2]: dpo-receive
3293  * fe80::fe:3eff:fe3e:9222/128
3294  *   unicast-ip6-chain
3295  *   [@0]: dpo-load-balance: [index:29 buckets:1 uRPF:28 to:[0:0]]
3296  *     [0] [@2]: dpo-receive: fe80::fe:3eff:fe3e:9222 on af_packet0
3297  * ff02::1/128
3298  *   unicast-ip6-chain
3299  *   [@0]: dpo-load-balance: [index:24 buckets:1 uRPF:23 to:[0:0]]
3300  *     [0] [@2]: dpo-receive
3301  * ff02::2/128
3302  *   unicast-ip6-chain
3303  *   [@0]: dpo-load-balance: [index:23 buckets:1 uRPF:22 to:[0:0]]
3304  *     [0] [@2]: dpo-receive
3305  * ff02::16/128
3306  *   unicast-ip6-chain
3307  *   [@0]: dpo-load-balance: [index:25 buckets:1 uRPF:24 to:[0:0]]
3308  *     [0] [@2]: dpo-receive
3309  * ff02::1:ff00:0/104
3310  *   unicast-ip6-chain
3311  *   [@0]: dpo-load-balance: [index:22 buckets:1 uRPF:21 to:[0:0]]
3312  *     [0] [@2]: dpo-receive
3313  * @cliexend
3314  * @endparblock
3315 ?*/
3316 /* *INDENT-OFF* */
3317 VLIB_CLI_COMMAND (set_ip6_flow_hash_command, static) =
3318 {
3319   .path = "set ip6 flow-hash",
3320   .short_help =
3321   "set ip6 flow-hash table <table-id> [src] [dst] [sport] [dport] [proto] [reverse]",
3322   .function = set_ip6_flow_hash_command_fn,
3323 };
3324 /* *INDENT-ON* */
3325
3326 static clib_error_t *
3327 show_ip6_local_command_fn (vlib_main_t * vm,
3328                            unformat_input_t * input, vlib_cli_command_t * cmd)
3329 {
3330   ip6_main_t *im = &ip6_main;
3331   ip_lookup_main_t *lm = &im->lookup_main;
3332   int i;
3333
3334   vlib_cli_output (vm, "Protocols handled by ip6_local");
3335   for (i = 0; i < ARRAY_LEN (lm->local_next_by_ip_protocol); i++)
3336     {
3337       if (lm->local_next_by_ip_protocol[i] != IP_LOCAL_NEXT_PUNT)
3338         vlib_cli_output (vm, "%d", i);
3339     }
3340   return 0;
3341 }
3342
3343
3344
3345 /*?
3346  * Display the set of protocols handled by the local IPv6 stack.
3347  *
3348  * @cliexpar
3349  * Example of how to display local protocol table:
3350  * @cliexstart{show ip6 local}
3351  * Protocols handled by ip6_local
3352  * 17
3353  * 43
3354  * 58
3355  * 115
3356  * @cliexend
3357 ?*/
3358 /* *INDENT-OFF* */
3359 VLIB_CLI_COMMAND (show_ip6_local, static) =
3360 {
3361   .path = "show ip6 local",
3362   .function = show_ip6_local_command_fn,
3363   .short_help = "show ip6 local",
3364 };
3365 /* *INDENT-ON* */
3366
3367 int
3368 vnet_set_ip6_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
3369                              u32 table_index)
3370 {
3371   vnet_main_t *vnm = vnet_get_main ();
3372   vnet_interface_main_t *im = &vnm->interface_main;
3373   ip6_main_t *ipm = &ip6_main;
3374   ip_lookup_main_t *lm = &ipm->lookup_main;
3375   vnet_classify_main_t *cm = &vnet_classify_main;
3376   ip6_address_t *if_addr;
3377
3378   if (pool_is_free_index (im->sw_interfaces, sw_if_index))
3379     return VNET_API_ERROR_NO_MATCHING_INTERFACE;
3380
3381   if (table_index != ~0 && pool_is_free_index (cm->tables, table_index))
3382     return VNET_API_ERROR_NO_SUCH_ENTRY;
3383
3384   vec_validate (lm->classify_table_index_by_sw_if_index, sw_if_index);
3385   lm->classify_table_index_by_sw_if_index[sw_if_index] = table_index;
3386
3387   if_addr = ip6_interface_first_address (ipm, sw_if_index);
3388
3389   if (NULL != if_addr)
3390     {
3391       fib_prefix_t pfx = {
3392         .fp_len = 128,
3393         .fp_proto = FIB_PROTOCOL_IP6,
3394         .fp_addr.ip6 = *if_addr,
3395       };
3396       u32 fib_index;
3397
3398       fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
3399                                                        sw_if_index);
3400
3401
3402       if (table_index != (u32) ~ 0)
3403         {
3404           dpo_id_t dpo = DPO_INVALID;
3405
3406           dpo_set (&dpo,
3407                    DPO_CLASSIFY,
3408                    DPO_PROTO_IP6,
3409                    classify_dpo_create (DPO_PROTO_IP6, table_index));
3410
3411           fib_table_entry_special_dpo_add (fib_index,
3412                                            &pfx,
3413                                            FIB_SOURCE_CLASSIFY,
3414                                            FIB_ENTRY_FLAG_NONE, &dpo);
3415           dpo_reset (&dpo);
3416         }
3417       else
3418         {
3419           fib_table_entry_special_remove (fib_index,
3420                                           &pfx, FIB_SOURCE_CLASSIFY);
3421         }
3422     }
3423
3424   return 0;
3425 }
3426
3427 static clib_error_t *
3428 set_ip6_classify_command_fn (vlib_main_t * vm,
3429                              unformat_input_t * input,
3430                              vlib_cli_command_t * cmd)
3431 {
3432   u32 table_index = ~0;
3433   int table_index_set = 0;
3434   u32 sw_if_index = ~0;
3435   int rv;
3436
3437   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3438     {
3439       if (unformat (input, "table-index %d", &table_index))
3440         table_index_set = 1;
3441       else if (unformat (input, "intfc %U", unformat_vnet_sw_interface,
3442                          vnet_get_main (), &sw_if_index))
3443         ;
3444       else
3445         break;
3446     }
3447
3448   if (table_index_set == 0)
3449     return clib_error_return (0, "classify table-index must be specified");
3450
3451   if (sw_if_index == ~0)
3452     return clib_error_return (0, "interface / subif must be specified");
3453
3454   rv = vnet_set_ip6_classify_intfc (vm, sw_if_index, table_index);
3455
3456   switch (rv)
3457     {
3458     case 0:
3459       break;
3460
3461     case VNET_API_ERROR_NO_MATCHING_INTERFACE:
3462       return clib_error_return (0, "No such interface");
3463
3464     case VNET_API_ERROR_NO_SUCH_ENTRY:
3465       return clib_error_return (0, "No such classifier table");
3466     }
3467   return 0;
3468 }
3469
3470 /*?
3471  * Assign a classification table to an interface. The classification
3472  * table is created using the '<em>classify table</em>' and '<em>classify session</em>'
3473  * commands. Once the table is create, use this command to filter packets
3474  * on an interface.
3475  *
3476  * @cliexpar
3477  * Example of how to assign a classification table to an interface:
3478  * @cliexcmd{set ip6 classify intfc GigabitEthernet2/0/0 table-index 1}
3479 ?*/
3480 /* *INDENT-OFF* */
3481 VLIB_CLI_COMMAND (set_ip6_classify_command, static) =
3482 {
3483   .path = "set ip6 classify",
3484   .short_help =
3485   "set ip6 classify intfc <interface> table-index <classify-idx>",
3486   .function = set_ip6_classify_command_fn,
3487 };
3488 /* *INDENT-ON* */
3489
3490 static clib_error_t *
3491 ip6_config (vlib_main_t * vm, unformat_input_t * input)
3492 {
3493   ip6_main_t *im = &ip6_main;
3494   uword heapsize = 0;
3495   u32 tmp;
3496   u32 nbuckets = 0;
3497
3498   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3499     {
3500       if (unformat (input, "hash-buckets %d", &tmp))
3501         nbuckets = tmp;
3502       else if (unformat (input, "heap-size %dm", &tmp))
3503         heapsize = ((u64) tmp) << 20;
3504       else if (unformat (input, "heap-size %dM", &tmp))
3505         heapsize = ((u64) tmp) << 20;
3506       else if (unformat (input, "heap-size %dg", &tmp))
3507         heapsize = ((u64) tmp) << 30;
3508       else if (unformat (input, "heap-size %dG", &tmp))
3509         heapsize = ((u64) tmp) << 30;
3510       else
3511         return clib_error_return (0, "unknown input '%U'",
3512                                   format_unformat_error, input);
3513     }
3514
3515   im->lookup_table_nbuckets = nbuckets;
3516   im->lookup_table_size = heapsize;
3517
3518   return 0;
3519 }
3520
3521 VLIB_EARLY_CONFIG_FUNCTION (ip6_config, "ip6");
3522
3523 /*
3524  * fd.io coding-style-patch-verification: ON
3525  *
3526  * Local Variables:
3527  * eval: (c-set-style "gnu")
3528  * End:
3529  */