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