0913cfd791c3246712ff49e2107a0d16bd1bbda2
[vpp.git] / vnet / vnet / adj / adj_nbr.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/adj/adj_nbr.h>
17 #include <vnet/adj/adj_internal.h>
18 #include <vnet/ethernet/arp_packet.h>
19 #include <vnet/fib/fib_walk.h>
20
21 /*
22  * Vector Hash tables of neighbour (traditional) adjacencies
23  *  Key: interface(for the vector index), address (and its proto),
24  *       link-type/ether-type.
25  */
26 static BVT(clib_bihash) **adj_nbr_tables[FIB_PROTOCOL_MAX];
27
28 // FIXME SIZE APPROPRIATELY. ASK DAVEB.
29 #define ADJ_NBR_DEFAULT_HASH_NUM_BUCKETS (64 * 64)
30 #define ADJ_NBR_DEFAULT_HASH_MEMORY_SIZE (32<<20)
31
32
33 #define ADJ_NBR_SET_KEY(_key, _lt, _nh)         \
34 {                                               \
35     _key.key[0] = (_nh)->as_u64[0];             \
36     _key.key[1] = (_nh)->as_u64[1];             \
37     _key.key[2] = (_lt);                        \
38 }
39
40 #define ADJ_NBR_ITF_OK(_proto, _itf)                    \
41     (((_itf) < vec_len(adj_nbr_tables[_proto])) &&      \
42      (NULL != adj_nbr_tables[_proto][sw_if_index]))
43
44 static void
45 adj_nbr_insert (fib_protocol_t nh_proto,
46                 fib_link_t link_type,
47                 const ip46_address_t *nh_addr,
48                 u32 sw_if_index,
49                 adj_index_t adj_index)
50 {
51     BVT(clib_bihash_kv) kv;
52
53     if (sw_if_index >= vec_len(adj_nbr_tables[nh_proto]))
54     {
55         vec_validate(adj_nbr_tables[nh_proto], sw_if_index);
56     }
57     if (NULL == adj_nbr_tables[nh_proto][sw_if_index])
58     {
59         adj_nbr_tables[nh_proto][sw_if_index] =
60             clib_mem_alloc_aligned(sizeof(BVT(clib_bihash)),
61                                    CLIB_CACHE_LINE_BYTES);
62         memset(adj_nbr_tables[nh_proto][sw_if_index],
63                0,
64                sizeof(BVT(clib_bihash)));
65
66         BV(clib_bihash_init) (adj_nbr_tables[nh_proto][sw_if_index],
67                               "Adjacency Neighbour table",
68                               ADJ_NBR_DEFAULT_HASH_NUM_BUCKETS,
69                               ADJ_NBR_DEFAULT_HASH_MEMORY_SIZE);
70     }
71
72     ADJ_NBR_SET_KEY(kv, link_type, nh_addr);
73     kv.value = adj_index;
74
75     BV(clib_bihash_add_del) (adj_nbr_tables[nh_proto][sw_if_index], &kv, 1);
76 }
77
78 void
79 adj_nbr_remove (fib_protocol_t nh_proto,
80                 fib_link_t link_type,
81                 const ip46_address_t *nh_addr,
82                 u32 sw_if_index)
83 {
84     BVT(clib_bihash_kv) kv;
85
86     if (!ADJ_NBR_ITF_OK(nh_proto, sw_if_index))
87         return;
88
89     ADJ_NBR_SET_KEY(kv, link_type, nh_addr);
90
91     BV(clib_bihash_add_del) (adj_nbr_tables[nh_proto][sw_if_index], &kv, 0);
92 }
93
94 static adj_index_t
95 adj_nbr_find (fib_protocol_t nh_proto,
96               fib_link_t link_type,
97               const ip46_address_t *nh_addr,
98               u32 sw_if_index)
99 {
100     BVT(clib_bihash_kv) kv;
101
102     ADJ_NBR_SET_KEY(kv, link_type, nh_addr);
103
104     if (!ADJ_NBR_ITF_OK(nh_proto, sw_if_index))
105         return (ADJ_INDEX_INVALID);
106
107     if (BV(clib_bihash_search)(adj_nbr_tables[nh_proto][sw_if_index],
108                                &kv, &kv) < 0)
109     {
110         return (ADJ_INDEX_INVALID);
111     }
112     else
113     {
114         return (kv.value);
115     }
116 }
117
118 static inline vlib_node_registration_t*
119 adj_get_nd_node (fib_protocol_t proto)
120 {
121     switch (proto) {
122     case FIB_PROTOCOL_IP4:
123         return (&ip4_arp_node);
124     case FIB_PROTOCOL_IP6:
125         return (&ip6_discover_neighbor_node);
126     case FIB_PROTOCOL_MPLS:
127         break;
128     }
129     ASSERT(0);
130     return (NULL);
131 }
132
133 static void
134 adj_ip4_nbr_probe (ip_adjacency_t *adj)
135 {
136     vnet_main_t * vnm = vnet_get_main();
137     ip4_main_t * im = &ip4_main;
138     ip_interface_address_t * ia;
139     ethernet_arp_header_t * h;
140     vnet_hw_interface_t * hi;
141     vnet_sw_interface_t * si;
142     ip4_address_t * src;
143     vlib_buffer_t * b;
144     vlib_main_t * vm;
145     u32 bi = 0;
146
147     vm = vlib_get_main();
148
149     si = vnet_get_sw_interface (vnm,
150                                 adj->rewrite_header.sw_if_index);
151
152     if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
153     {
154         return;
155     }
156
157     src =
158       ip4_interface_address_matching_destination(im,
159                                                  &adj->sub_type.nbr.next_hop.ip4,
160                                                  adj->rewrite_header.sw_if_index,
161                                                  &ia);
162     if (! src)
163     {
164         return;
165     }
166
167     h = vlib_packet_template_get_packet (vm, &im->ip4_arp_request_packet_template, &bi);
168
169     hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
170
171     clib_memcpy (h->ip4_over_ethernet[0].ethernet,
172                  hi->hw_address,
173                  sizeof (h->ip4_over_ethernet[0].ethernet));
174
175     h->ip4_over_ethernet[0].ip4 = src[0];
176     h->ip4_over_ethernet[1].ip4 = adj->sub_type.nbr.next_hop.ip4;
177
178     b = vlib_get_buffer (vm, bi);
179     vnet_buffer (b)->sw_if_index[VLIB_RX] =
180       vnet_buffer (b)->sw_if_index[VLIB_TX] =
181           adj->rewrite_header.sw_if_index;
182
183     /* Add encapsulation string for software interface (e.g. ethernet header). */
184     vnet_rewrite_one_header (adj[0], h, sizeof (ethernet_header_t));
185     vlib_buffer_advance (b, -adj->rewrite_header.data_bytes);
186
187     {
188         vlib_frame_t * f = vlib_get_frame_to_node (vm, hi->output_node_index);
189         u32 * to_next = vlib_frame_vector_args (f);
190         to_next[0] = bi;
191         f->n_vectors = 1;
192         vlib_put_frame_to_node (vm, hi->output_node_index, f);
193     }
194 }
195
196 static void
197 adj_ip6_nbr_probe (ip_adjacency_t *adj)
198 {
199     icmp6_neighbor_solicitation_header_t * h;
200     vnet_main_t * vnm = vnet_get_main();
201     ip6_main_t * im = &ip6_main;
202     ip_interface_address_t * ia;
203     ip6_address_t * dst, *src;
204     vnet_hw_interface_t * hi;
205     vnet_sw_interface_t * si;
206     vlib_buffer_t * b;
207     int bogus_length;
208     vlib_main_t * vm;
209     u32 bi = 0;
210
211     vm = vlib_get_main();
212
213     si = vnet_get_sw_interface(vnm, adj->rewrite_header.sw_if_index);
214     dst = &adj->sub_type.nbr.next_hop.ip6;
215
216     if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
217     {
218         return;
219     }
220     src = ip6_interface_address_matching_destination(im, dst,
221                                                      adj->rewrite_header.sw_if_index,
222                                                      &ia);
223     if (! src)
224     {
225        return;
226     }
227
228     h = vlib_packet_template_get_packet(vm,
229                                         &im->discover_neighbor_packet_template,
230                                         &bi);
231
232     hi = vnet_get_sup_hw_interface(vnm, adj->rewrite_header.sw_if_index);
233
234     h->ip.dst_address.as_u8[13] = dst->as_u8[13];
235     h->ip.dst_address.as_u8[14] = dst->as_u8[14];
236     h->ip.dst_address.as_u8[15] = dst->as_u8[15];
237     h->ip.src_address = src[0];
238     h->neighbor.target_address = dst[0];
239
240     clib_memcpy (h->link_layer_option.ethernet_address,
241                  hi->hw_address,
242                  vec_len(hi->hw_address));
243
244     h->neighbor.icmp.checksum = 
245         ip6_tcp_udp_icmp_compute_checksum(vm, 0, &h->ip, &bogus_length);
246     ASSERT(bogus_length == 0);
247
248     b = vlib_get_buffer (vm, bi);
249     vnet_buffer (b)->sw_if_index[VLIB_RX] =
250         vnet_buffer (b)->sw_if_index[VLIB_TX] =
251           adj->rewrite_header.sw_if_index;
252
253     /* Add encapsulation string for software interface (e.g. ethernet header). */
254     vnet_rewrite_one_header(adj[0], h, sizeof (ethernet_header_t));
255     vlib_buffer_advance(b, -adj->rewrite_header.data_bytes);
256
257     {
258         vlib_frame_t * f = vlib_get_frame_to_node(vm, hi->output_node_index);
259         u32 * to_next = vlib_frame_vector_args(f);
260         to_next[0] = bi;
261         f->n_vectors = 1;
262         vlib_put_frame_to_node(vm, hi->output_node_index, f);
263     }
264 }
265
266 static ip_adjacency_t*
267 adj_nbr_alloc (fib_protocol_t nh_proto,
268                fib_link_t link_type,
269                const ip46_address_t *nh_addr,
270                u32 sw_if_index)
271 {
272     ip_adjacency_t *adj;
273
274     adj = adj_alloc(nh_proto);
275
276     adj_nbr_insert(nh_proto, link_type, nh_addr,
277                    sw_if_index,
278                    adj->heap_handle);
279
280     /*
281      * since we just added the ADJ we have no rewrite string for it,
282      * so its for ARP
283      */
284     adj->lookup_next_index = IP_LOOKUP_NEXT_ARP;
285     adj->sub_type.nbr.next_hop = *nh_addr;
286     adj->ia_link = link_type;
287     adj->ia_nh_proto = nh_proto;
288     memset(&adj->sub_type.midchain.next_dpo, 0,
289            sizeof(adj->sub_type.midchain.next_dpo));
290
291     return (adj);
292 }
293
294 /*
295  * adj_add_for_nbr
296  *
297  * Add an adjacency for the neighbour requested.
298  *
299  * The key for an adj is:
300  *   - the Next-hops protocol (i.e. v4 or v6)
301  *   - the address of the next-hop
302  *   - the interface the next-hop is reachable through
303  *   - fib_index; this is broken. i will fix it.
304  *     the adj lookup currently occurs in the FIB.
305  */
306 adj_index_t
307 adj_nbr_add_or_lock (fib_protocol_t nh_proto,
308                      fib_link_t link_type,
309                      const ip46_address_t *nh_addr,
310                      u32 sw_if_index)
311 {
312     adj_index_t adj_index;
313     ip_adjacency_t *adj;
314
315     adj_index = adj_nbr_find(nh_proto, link_type, nh_addr, sw_if_index);
316
317     if (ADJ_INDEX_INVALID == adj_index)
318     {
319         adj = adj_nbr_alloc(nh_proto, link_type, nh_addr, sw_if_index);
320
321         /*
322          * If there is no next-hop, this is the 'auto-adj' used on p2p
323          * links instead of a glean.
324          */
325         if (ip46_address_is_zero(nh_addr))
326         {
327             adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
328
329             vnet_rewrite_for_sw_interface(vnet_get_main(),
330                                           adj_fib_link_2_vnet(link_type),
331                                           sw_if_index,
332                                           adj_get_rewrite_node(link_type)->index,
333                                           VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST,
334                                           &adj->rewrite_header,
335                                           sizeof (adj->rewrite_data));
336         }
337         else
338         {
339             vnet_rewrite_for_sw_interface(vnet_get_main(),
340                                           adj_fib_proto_2_nd(nh_proto),
341                                           sw_if_index,
342                                           adj_get_nd_node(nh_proto)->index,
343                                           VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST,
344                                           &adj->rewrite_header,
345                                           sizeof (adj->rewrite_data));
346
347             switch (nh_proto)
348             {
349             case FIB_PROTOCOL_IP4:
350                 adj_ip4_nbr_probe(adj);
351                 break;
352             case FIB_PROTOCOL_IP6:
353                 adj_ip6_nbr_probe(adj);
354                 break;
355             case FIB_PROTOCOL_MPLS:
356                 break;
357             }
358         }
359     }
360     else
361     {
362         adj = adj_get(adj_index);
363     }
364
365     adj_lock(adj->heap_handle);
366
367     return (adj->heap_handle);
368 }
369
370 adj_index_t
371 adj_nbr_add_or_lock_w_rewrite (fib_protocol_t nh_proto,
372                                fib_link_t link_type,
373                                const ip46_address_t *nh_addr,
374                                u32 sw_if_index,
375                                u8 *rewrite)
376 {
377     adj_index_t adj_index;
378     ip_adjacency_t *adj;
379
380     adj_index = adj_nbr_find(nh_proto, link_type, nh_addr, sw_if_index);
381
382     if (ADJ_INDEX_INVALID == adj_index)
383     {
384         adj = adj_nbr_alloc(nh_proto, link_type, nh_addr, sw_if_index);
385         adj->rewrite_header.sw_if_index = sw_if_index;
386     }
387     else
388     {
389         adj = adj_get(adj_index);
390     }
391
392     adj_lock(adj->heap_handle);
393     adj_nbr_update_rewrite(adj->heap_handle, rewrite);
394
395     return (adj->heap_handle);
396 }
397
398 /**
399  * adj_nbr_update_rewrite
400  *
401  * Update the adjacency's rewrite string. A NULL string implies the
402  * rewirte is reset (i.e. when ARP/ND etnry is gone).
403  * NB: the adj being updated may be handling traffic in the DP.
404  */
405 void
406 adj_nbr_update_rewrite (adj_index_t adj_index,
407                         u8 *rewrite)
408 {
409     ip_adjacency_t *adj;
410
411     ASSERT(ADJ_INDEX_INVALID != adj_index);
412
413     adj = adj_get(adj_index);
414
415     if (NULL != rewrite)
416     {
417         /*
418          * new rewrite provided.
419          * use a dummy rewrite header to get the interface to print into.
420          */
421         ip_adjacency_t dummy;
422
423         vnet_rewrite_for_sw_interface(vnet_get_main(),
424                                       adj_fib_link_2_vnet(adj->ia_link),
425                                       adj->rewrite_header.sw_if_index,
426                                       adj_get_rewrite_node(adj->ia_link)->index,
427                                       rewrite,
428                                       &dummy.rewrite_header,
429                                       sizeof (dummy.rewrite_data));
430
431         if (IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index)
432         {
433             /*
434              * this is an update of an existing rewrite.
435              * we can't just paste in the new rewrite as that is not atomic.
436              * So we briefly swap the ADJ to ARP type, paste, then swap back.
437              */
438             adj->lookup_next_index = IP_LOOKUP_NEXT_ARP;
439             CLIB_MEMORY_BARRIER();
440         }
441         /*
442          * else
443          *   this is the first time the rewrite is added.
444          *   paste it on then swap the next type.
445          */
446         clib_memcpy(&adj->rewrite_header,
447                     &dummy.rewrite_header,
448                     VLIB_BUFFER_PRE_DATA_SIZE);
449
450         adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
451     }
452     else
453     {
454         /*
455          * clear the rewrite.
456          */
457         adj->lookup_next_index = IP_LOOKUP_NEXT_ARP;
458         CLIB_MEMORY_BARRIER();
459
460         adj->rewrite_header.data_bytes = 0;
461     }
462
463     /*
464      * time for walkies fido.
465      * The link type MPLS Adj never has children. So if it is this adj
466      * that is updated, we need to walk from its IP sibling.
467      */
468     if (FIB_LINK_MPLS == adj->ia_link)
469     {
470         adj_index = adj_nbr_find(adj->ia_nh_proto,
471                                  fib_proto_to_link(adj->ia_nh_proto),
472                                  &adj->sub_type.nbr.next_hop,
473                                  adj->rewrite_header.sw_if_index);
474
475         ASSERT(ADJ_INDEX_INVALID != adj_index);
476     }
477
478     fib_node_back_walk_ctx_t bw_ctx = {
479         .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE,
480         /*
481          * This walk only needs to go back one level, but there is no control here.
482          * the first receiving fib_entry_t will quash the walk
483          */
484     };
485
486     fib_walk_sync(FIB_NODE_TYPE_ADJ, adj_index, &bw_ctx);
487 }
488
489 typedef struct adj_db_count_ctx_t_ {
490     u64 count;
491 } adj_db_count_ctx_t;
492
493 static void
494 adj_db_count (BVT(clib_bihash_kv) * kvp,
495               void *arg)
496 {
497     adj_db_count_ctx_t * ctx = arg;
498     ctx->count++;
499 }
500
501 u32
502 adj_nbr_db_size (void)
503 {
504     adj_db_count_ctx_t ctx = {
505         .count = 0,
506     };
507     fib_protocol_t proto;
508     u32 sw_if_index = 0;
509
510     for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
511     {
512         vec_foreach_index(sw_if_index, adj_nbr_tables[proto])
513         {
514             if (NULL != adj_nbr_tables[proto][sw_if_index])
515             {
516                 BV(clib_bihash_foreach_key_value_pair) (
517                     adj_nbr_tables[proto][sw_if_index],
518                     adj_db_count,
519                     &ctx);
520             }
521         }
522     }
523     return (ctx.count);
524 }
525
526 /**
527  * Context for the state change walk of the DB
528  */
529 typedef struct adj_nbr_interface_state_change_ctx_t_
530 {
531     /**
532      * Flags passed from the vnet notifiy function
533      */
534     int flags;
535 } adj_nbr_interface_state_change_ctx_t;
536
537 static void
538 adj_nbr_interface_state_change_one (BVT(clib_bihash_kv) * kvp,
539                                     void *arg)
540 {
541     /*
542      * Back walk the graph to inform the forwarding entries
543      * that this interface state has changed.
544      */
545     adj_nbr_interface_state_change_ctx_t *ctx = arg;
546
547     fib_node_back_walk_ctx_t bw_ctx = {
548         .fnbw_reason = (ctx->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
549                         FIB_NODE_BW_REASON_FLAG_INTERFACE_UP :
550                         FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN),
551     };
552
553     fib_walk_sync(FIB_NODE_TYPE_ADJ, kvp->value, &bw_ctx);
554 }
555
556 static clib_error_t *
557 adj_nbr_interface_state_change (vnet_main_t * vnm,
558                                 u32 sw_if_index,
559                                 u32 flags)
560 {
561     fib_protocol_t proto;
562
563     /*
564      * walk each adj on the interface and trigger a walk from that adj
565      */
566     for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
567     {
568         if (!ADJ_NBR_ITF_OK(proto, sw_if_index))
569             continue;
570
571         adj_nbr_interface_state_change_ctx_t ctx = {
572             .flags = flags,
573         };
574
575         BV(clib_bihash_foreach_key_value_pair) (
576             adj_nbr_tables[proto][sw_if_index],
577             adj_nbr_interface_state_change_one,
578             &ctx);
579     }
580
581     return (NULL);
582 }
583
584 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(adj_nbr_interface_state_change);
585
586 static void
587 adj_nbr_interface_delete_one (BVT(clib_bihash_kv) * kvp,
588                               void *arg)
589 {
590     /*
591      * Back walk the graph to inform the forwarding entries
592      * that this interface has been deleted.
593      */
594     fib_node_back_walk_ctx_t bw_ctx = {
595         .fnbw_reason = FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE,
596     };
597
598     fib_walk_sync(FIB_NODE_TYPE_ADJ, kvp->value, &bw_ctx);
599 }
600
601 /**
602  * adj_nbr_interface_add_del
603  *
604  * Registered to receive interface Add and delete notifications
605  */
606 static clib_error_t *
607 adj_nbr_interface_add_del (vnet_main_t * vnm,
608                            u32 sw_if_index,
609                            u32 is_add)
610 {
611     fib_protocol_t proto;
612
613     if (is_add)
614     {
615         /*
616          * not interested in interface additions. we will not back walk
617          * to resolve paths through newly added interfaces. Why? The control
618          * plane should have the brains to add interfaces first, then routes.
619          * So the case where there are paths with a interface that matches
620          * one just created is the case where the path resolved through an
621          * interface that was deleted, and still has not been removed. The
622          * new interface added, is NO GUARANTEE that the interface being
623          * added now, even though it may have the same sw_if_index, is the
624          * same interface that the path needs. So tough!
625          * If the control plane wants these routes to resolve it needs to
626          * remove and add them again.
627          */
628         return (NULL);
629     }
630
631     for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
632     {
633         if (!ADJ_NBR_ITF_OK(proto, sw_if_index))
634             continue;
635
636         BV(clib_bihash_foreach_key_value_pair) (
637             adj_nbr_tables[proto][sw_if_index],
638             adj_nbr_interface_delete_one,
639             NULL);
640     }
641
642     return (NULL);
643    
644 }
645
646 VNET_SW_INTERFACE_ADD_DEL_FUNCTION(adj_nbr_interface_add_del);
647
648
649 static void
650 adj_nbr_show_one (BVT(clib_bihash_kv) * kvp,
651                   void *arg)
652 {
653     vlib_cli_output (arg, "[@%d]  %U",
654                      kvp->value,
655                      format_ip_adjacency,
656                      vnet_get_main(), kvp->value,
657                      FORMAT_IP_ADJACENCY_NONE);
658 }
659
660 static clib_error_t *
661 adj_nbr_show (vlib_main_t * vm,
662               unformat_input_t * input,
663               vlib_cli_command_t * cmd)
664 {
665     adj_index_t ai = ADJ_INDEX_INVALID;
666
667     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
668     {
669         if (unformat (input, "%d", &ai))
670             ;
671         else
672             break;
673     }
674
675     if (ADJ_INDEX_INVALID != ai)
676     {
677         vlib_cli_output (vm, "[@%d] %U",
678                          ai,
679
680                          format_ip_adjacency,
681                          vnet_get_main(), ai,
682                          FORMAT_IP_ADJACENCY_DETAIL);
683     }
684     else
685     {
686         fib_protocol_t proto;
687
688         for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
689         {
690             u32 sw_if_index;
691
692             vec_foreach_index(sw_if_index, adj_nbr_tables[proto])
693             {
694                 if (!ADJ_NBR_ITF_OK(proto, sw_if_index))
695                     continue;
696
697                 BV(clib_bihash_foreach_key_value_pair) (
698                     adj_nbr_tables[proto][sw_if_index],
699                     adj_nbr_show_one,
700                     vm);
701             }
702         }
703     }
704
705     return 0;
706 }
707
708 VLIB_CLI_COMMAND (ip4_show_fib_command, static) = {
709     .path = "show adj nbr",
710     .short_help = "show adj nbr [<adj_index>] [sw_if_index <index>]",
711     .function = adj_nbr_show,
712 };
713
714 u8*
715 format_adj_nbr_incomplete (u8* s, va_list *ap)
716 {
717     index_t index = va_arg(ap, index_t);
718     CLIB_UNUSED(u32 indent) = va_arg(ap, u32);
719     vnet_main_t * vnm = vnet_get_main();
720     ip_adjacency_t * adj = adj_get(index);
721
722     s = format (s, "arp-%U", format_fib_link, adj->ia_link);
723     s = format (s, ": via %U",
724                 format_ip46_address, &adj->sub_type.nbr.next_hop, IP46_TYPE_ANY);
725     s = format (s, " %U",
726                 format_vnet_sw_interface_name,
727                 vnm,
728                 vnet_get_sw_interface(vnm,
729                                       adj->rewrite_header.sw_if_index));
730
731     return (s);
732 }
733
734 u8*
735 format_adj_nbr (u8* s, va_list *ap)
736 {
737     index_t index = va_arg(ap, index_t);
738     CLIB_UNUSED(u32 indent) = va_arg(ap, u32);
739     vnet_main_t * vnm = vnet_get_main();
740     ip_adjacency_t * adj = adj_get(index);
741
742     s = format (s, "%U", format_fib_link, adj->ia_link);
743     s = format (s, " via %U ",
744                 format_ip46_address, &adj->sub_type.nbr.next_hop, IP46_TYPE_ANY);
745     s = format (s, "%U",
746                 format_vnet_rewrite,
747                 vnm->vlib_main, &adj->rewrite_header, sizeof (adj->rewrite_data), 0);
748
749     return (s);
750 }
751
752 static void
753 adj_dpo_lock (dpo_id_t *dpo)
754 {
755     adj_lock(dpo->dpoi_index);
756 }
757 static void
758 adj_dpo_unlock (dpo_id_t *dpo)
759 {
760     adj_unlock(dpo->dpoi_index);
761 }
762
763 const static dpo_vft_t adj_nbr_dpo_vft = {
764     .dv_lock = adj_dpo_lock,
765     .dv_unlock = adj_dpo_unlock,
766     .dv_format = format_adj_nbr,
767 };
768 const static dpo_vft_t adj_nbr_incompl_dpo_vft = {
769     .dv_lock = adj_dpo_lock,
770     .dv_unlock = adj_dpo_unlock,
771     .dv_format = format_adj_nbr_incomplete,
772 };
773
774 /**
775  * @brief The per-protocol VLIB graph nodes that are assigned to an adjacency
776  *        object.
777  *
778  * this means that these graph nodes are ones from which a nbr is the
779  * parent object in the DPO-graph.
780  */
781 const static char* const nbr_ip4_nodes[] =
782 {
783     "ip4-rewrite-transit",
784     NULL,
785 };
786 const static char* const nbr_ip6_nodes[] =
787 {
788     "ip6-rewrite",
789     NULL,
790 };
791 const static char* const nbr_mpls_nodes[] =
792 {
793     "mpls-output",
794     NULL,
795 };
796 const static char* const nbr_ethernet_nodes[] =
797 {
798     "adj-l2-rewrite",
799     NULL,
800 };
801 const static char* const * const nbr_nodes[DPO_PROTO_NUM] =
802 {
803     [DPO_PROTO_IP4]  = nbr_ip4_nodes,
804     [DPO_PROTO_IP6]  = nbr_ip6_nodes,
805     [DPO_PROTO_MPLS] = nbr_mpls_nodes,
806     [DPO_PROTO_ETHERNET] = nbr_ethernet_nodes,
807 };
808
809 const static char* const nbr_incomplete_ip4_nodes[] =
810 {
811     "ip4-arp",
812     NULL,
813 };
814 const static char* const nbr_incomplete_ip6_nodes[] =
815 {
816     "ip6-discover-neighbor",
817     NULL,
818 };
819 const static char* const nbr_incomplete_mpls_nodes[] =
820 {
821     "mpls-adj-incomplete",
822     NULL,
823 };
824
825 const static char* const * const nbr_incomplete_nodes[DPO_PROTO_NUM] =
826 {
827     [DPO_PROTO_IP4]  = nbr_incomplete_ip4_nodes,
828     [DPO_PROTO_IP6]  = nbr_incomplete_ip6_nodes,
829     [DPO_PROTO_MPLS] = nbr_incomplete_mpls_nodes,
830 };
831
832 void
833 adj_nbr_module_init (void)
834 {
835     dpo_register(DPO_ADJACENCY,
836                  &adj_nbr_dpo_vft,
837                  nbr_nodes);
838     dpo_register(DPO_ADJACENCY_INCOMPLETE,
839                  &adj_nbr_incompl_dpo_vft,
840                  nbr_incomplete_nodes);
841 }