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