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