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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
22 * Vector Hash tables of neighbour (traditional) adjacencies
23 * Key: interface(for the vector index), address (and its proto),
24 * link-type/ether-type.
26 static BVT(clib_bihash) **adj_nbr_tables[FIB_PROTOCOL_MAX];
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)
33 #define ADJ_NBR_SET_KEY(_key, _lt, _nh) \
35 _key.key[0] = (_nh)->as_u64[0]; \
36 _key.key[1] = (_nh)->as_u64[1]; \
37 _key.key[2] = (_lt); \
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]))
45 adj_nbr_insert (fib_protocol_t nh_proto,
46 vnet_link_t link_type,
47 const ip46_address_t *nh_addr,
49 adj_index_t adj_index)
51 BVT(clib_bihash_kv) kv;
53 if (sw_if_index >= vec_len(adj_nbr_tables[nh_proto]))
55 vec_validate(adj_nbr_tables[nh_proto], sw_if_index);
57 if (NULL == adj_nbr_tables[nh_proto][sw_if_index])
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],
64 sizeof(BVT(clib_bihash)));
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);
72 ADJ_NBR_SET_KEY(kv, link_type, nh_addr);
75 BV(clib_bihash_add_del) (adj_nbr_tables[nh_proto][sw_if_index], &kv, 1);
79 adj_nbr_remove (fib_protocol_t nh_proto,
80 vnet_link_t link_type,
81 const ip46_address_t *nh_addr,
84 BVT(clib_bihash_kv) kv;
86 if (!ADJ_NBR_ITF_OK(nh_proto, sw_if_index))
89 ADJ_NBR_SET_KEY(kv, link_type, nh_addr);
91 BV(clib_bihash_add_del) (adj_nbr_tables[nh_proto][sw_if_index], &kv, 0);
95 adj_nbr_find (fib_protocol_t nh_proto,
96 vnet_link_t link_type,
97 const ip46_address_t *nh_addr,
100 BVT(clib_bihash_kv) kv;
102 ADJ_NBR_SET_KEY(kv, link_type, nh_addr);
104 if (!ADJ_NBR_ITF_OK(nh_proto, sw_if_index))
105 return (ADJ_INDEX_INVALID);
107 if (BV(clib_bihash_search)(adj_nbr_tables[nh_proto][sw_if_index],
110 return (ADJ_INDEX_INVALID);
119 adj_get_nd_node (fib_protocol_t proto)
122 case FIB_PROTOCOL_IP4:
123 return (ip4_arp_node.index);
124 case FIB_PROTOCOL_IP6:
125 return (ip6_discover_neighbor_node.index);
126 case FIB_PROTOCOL_MPLS:
130 return (ip4_arp_node.index);
133 static ip_adjacency_t*
134 adj_nbr_alloc (fib_protocol_t nh_proto,
135 vnet_link_t link_type,
136 const ip46_address_t *nh_addr,
141 adj = adj_alloc(nh_proto);
143 adj_nbr_insert(nh_proto, link_type, nh_addr,
148 * since we just added the ADJ we have no rewrite string for it,
151 adj->lookup_next_index = IP_LOOKUP_NEXT_ARP;
152 adj->sub_type.nbr.next_hop = *nh_addr;
153 adj->ia_link = link_type;
154 adj->ia_nh_proto = nh_proto;
155 adj->rewrite_header.sw_if_index = sw_if_index;
156 memset(&adj->sub_type.midchain.next_dpo, 0,
157 sizeof(adj->sub_type.midchain.next_dpo));
165 * Add an adjacency for the neighbour requested.
167 * The key for an adj is:
168 * - the Next-hops protocol (i.e. v4 or v6)
169 * - the address of the next-hop
170 * - the interface the next-hop is reachable through
173 adj_nbr_add_or_lock (fib_protocol_t nh_proto,
174 vnet_link_t link_type,
175 const ip46_address_t *nh_addr,
178 adj_index_t adj_index;
181 adj_index = adj_nbr_find(nh_proto, link_type, nh_addr, sw_if_index);
183 if (ADJ_INDEX_INVALID == adj_index)
187 vnm = vnet_get_main();
188 adj = adj_nbr_alloc(nh_proto, link_type, nh_addr, sw_if_index);
189 adj_index = adj_get_index(adj);
192 vnet_rewrite_init(vnm, sw_if_index,
193 adj_get_nd_node(nh_proto),
194 vnet_tx_node_index_for_sw_interface(vnm, sw_if_index),
195 &adj->rewrite_header);
198 * we need a rewrite where the destination IP address is converted
199 * to the appropriate link-layer address. This is interface specific.
200 * So ask the interface to do it.
202 vnet_update_adjacency_for_sw_interface(vnm, sw_if_index, adj_index);
213 adj_nbr_add_or_lock_w_rewrite (fib_protocol_t nh_proto,
214 vnet_link_t link_type,
215 const ip46_address_t *nh_addr,
219 adj_index_t adj_index;
222 adj_index = adj_nbr_find(nh_proto, link_type, nh_addr, sw_if_index);
224 if (ADJ_INDEX_INVALID == adj_index)
226 adj = adj_nbr_alloc(nh_proto, link_type, nh_addr, sw_if_index);
227 adj->rewrite_header.sw_if_index = sw_if_index;
231 adj = adj_get(adj_index);
234 adj_lock(adj_get_index(adj));
235 adj_nbr_update_rewrite(adj_get_index(adj),
236 ADJ_NBR_REWRITE_FLAG_COMPLETE,
239 return (adj_get_index(adj));
243 * adj_nbr_update_rewrite
245 * Update the adjacency's rewrite string. A NULL string implies the
246 * rewirte is reset (i.e. when ARP/ND etnry is gone).
247 * NB: the adj being updated may be handling traffic in the DP.
250 adj_nbr_update_rewrite (adj_index_t adj_index,
251 adj_nbr_rewrite_flag_t flags,
256 ASSERT(ADJ_INDEX_INVALID != adj_index);
258 adj = adj_get(adj_index);
260 if (flags & ADJ_NBR_REWRITE_FLAG_COMPLETE)
263 * update the adj's rewrite string and build the arc
264 * from the rewrite node to the interface's TX node
266 adj_nbr_update_rewrite_internal(adj, IP_LOOKUP_NEXT_REWRITE,
267 adj_get_rewrite_node(adj->ia_link),
268 vnet_tx_node_index_for_sw_interface(
270 adj->rewrite_header.sw_if_index),
275 adj_nbr_update_rewrite_internal(adj, IP_LOOKUP_NEXT_ARP,
276 adj_get_nd_node(adj->ia_nh_proto),
277 vnet_tx_node_index_for_sw_interface(
279 adj->rewrite_header.sw_if_index),
285 * adj_nbr_update_rewrite_internal
287 * Update the adjacency's rewrite string. A NULL string implies the
288 * rewirte is reset (i.e. when ARP/ND etnry is gone).
289 * NB: the adj being updated may be handling traffic in the DP.
292 adj_nbr_update_rewrite_internal (ip_adjacency_t *adj,
302 vm = vlib_get_main();
303 old_next = adj->lookup_next_index;
305 walk_ai = adj_get_index(adj);
306 if (VNET_LINK_MPLS == adj->ia_link)
309 * The link type MPLS has no children in the control plane graph, it only
310 * has children in the data-palne graph. The backwalk is up the former.
311 * So we need to walk from its IP cousin.
313 walk_ai = adj_nbr_find(adj->ia_nh_proto,
314 fib_proto_to_link(adj->ia_nh_proto),
315 &adj->sub_type.nbr.next_hop,
316 adj->rewrite_header.sw_if_index);
320 * Updating a rewrite string is not atomic;
321 * - the rewrite string is too long to write in one instruction
322 * - when swapping from incomplete to complete, we also need to update
323 * the VLIB graph next-index of the adj.
324 * ideally we would only want to suspend forwarding via this adj whilst we
325 * do this, but we do not have that level of granularity - it's suspend all
326 * worker threads or nothing.
327 * The other chioces are:
328 * - to mark the adj down and back walk so child load-balances drop this adj
330 * - update the next_node index of this adj to point to error-drop
331 * both of which will mean for MAC change we will drop for this adj
332 * which is not acceptable. However, when the adj changes type (from
333 * complete to incomplete and vice-versa) the child DPOs, which have the
334 * VLIB graph next node index, will be sending packets to the wrong graph
335 * node. So from the options above, updating the next_node of the adj to
336 * be drop will work, but it relies on each graph node v4/v6/mpls, rewrite/
337 * arp/midchain always be valid w.r.t. a mis-match of adj type and node type
338 * (i.e. a rewrite adj in the arp node). This is not enforcable. Getting it
339 * wrong will lead to hard to find bugs since its a race condition. So we
340 * choose the more reliable method of updating the children to use the drop,
341 * then switching adj's type, then updating the children again. Did I mention
342 * that this doesn't happen often...
343 * So we need to distinguish between the two cases:
345 * 2 - adj type change
347 if (old_next != adj_next_index &&
348 ADJ_INDEX_INVALID != walk_ai)
351 * the adj is changing type. we need to fix all children so that they
352 * stack momentarily on a drop, while the adj changes. If we don't do
353 * this the children will send packets to a VLIB graph node that does
354 * not correspond to the adj's type - and it goes downhill from there.
356 fib_node_back_walk_ctx_t bw_ctx = {
357 .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_DOWN,
359 * force this walk to be synchrous. if we don't and a node in the graph
360 * (a heavily shared path-list) chooses to back-ground the walk (make it
361 * async) then it will pause and we will do the adj update below, before
362 * all the children are updated. not good.
364 .fnbw_flags = FIB_NODE_BW_FLAG_FORCE_SYNC,
367 fib_walk_sync(FIB_NODE_TYPE_ADJ, walk_ai, &bw_ctx);
371 * If we are just updating the MAC string of the adj (which we also can't
372 * do atomically), then we need to stop packets switching through the adj.
373 * We can't do that on a per-adj basis, so it's all the packets.
374 * If we are updating the type, and we walked back to the children above,
375 * then this barrier serves to flush the queues/frames.
377 vlib_worker_thread_barrier_sync(vm);
379 adj->lookup_next_index = adj_next_index;
384 * new rewrite provided.
385 * fill in the adj's rewrite string, and build the VLIB graph arc.
387 vnet_rewrite_set_data_internal(&adj->rewrite_header,
388 sizeof(adj->rewrite_data),
395 vnet_rewrite_clear_data_internal(&adj->rewrite_header,
396 sizeof(adj->rewrite_data));
398 adj->rewrite_header.node_index = this_node;
399 adj->rewrite_header.next_index = vlib_node_add_next(vlib_get_main(),
404 * done with the rewirte update - let the workers loose.
406 vlib_worker_thread_barrier_release(vm);
408 if (old_next != adj->lookup_next_index &&
409 ADJ_INDEX_INVALID != walk_ai)
412 * backwalk to the children so they can stack on the now updated
415 fib_node_back_walk_ctx_t bw_ctx = {
416 .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE,
419 fib_walk_sync(FIB_NODE_TYPE_ADJ, walk_ai, &bw_ctx);
423 typedef struct adj_db_count_ctx_t_ {
425 } adj_db_count_ctx_t;
428 adj_db_count (BVT(clib_bihash_kv) * kvp,
431 adj_db_count_ctx_t * ctx = arg;
436 adj_nbr_db_size (void)
438 adj_db_count_ctx_t ctx = {
441 fib_protocol_t proto;
444 for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
446 vec_foreach_index(sw_if_index, adj_nbr_tables[proto])
448 if (NULL != adj_nbr_tables[proto][sw_if_index])
450 BV(clib_bihash_foreach_key_value_pair) (
451 adj_nbr_tables[proto][sw_if_index],
461 * @brief Context for a walk of the adjacency neighbour DB
463 typedef struct adj_walk_ctx_t_
465 adj_walk_cb_t awc_cb;
470 adj_nbr_walk_cb (BVT(clib_bihash_kv) * kvp,
473 adj_walk_ctx_t *ctx = arg;
475 // FIXME: can't stop early...
476 ctx->awc_cb(kvp->value, ctx->awc_ctx);
480 adj_nbr_walk (u32 sw_if_index,
481 fib_protocol_t adj_nh_proto,
485 if (!ADJ_NBR_ITF_OK(adj_nh_proto, sw_if_index))
488 adj_walk_ctx_t awc = {
493 BV(clib_bihash_foreach_key_value_pair) (
494 adj_nbr_tables[adj_nh_proto][sw_if_index],
500 * @brief Context for a walk of the adjacency neighbour DB
502 typedef struct adj_walk_nh_ctx_t_
504 adj_walk_cb_t awc_cb;
506 const ip46_address_t *awc_nh;
510 adj_nbr_walk_nh_cb (BVT(clib_bihash_kv) * kvp,
514 adj_walk_nh_ctx_t *ctx = arg;
516 adj = adj_get(kvp->value);
518 if (!ip46_address_cmp(&adj->sub_type.nbr.next_hop, ctx->awc_nh))
519 ctx->awc_cb(kvp->value, ctx->awc_ctx);
523 * @brief Walk adjacencies on a link with a given v4 next-hop.
524 * that is visit the adjacencies with different link types.
527 adj_nbr_walk_nh4 (u32 sw_if_index,
528 const ip4_address_t *addr,
532 if (!ADJ_NBR_ITF_OK(FIB_PROTOCOL_IP4, sw_if_index))
535 ip46_address_t nh = {
539 adj_walk_nh_ctx_t awc = {
545 BV(clib_bihash_foreach_key_value_pair) (
546 adj_nbr_tables[FIB_PROTOCOL_IP4][sw_if_index],
552 * @brief Walk adjacencies on a link with a given v6 next-hop.
553 * that is visit the adjacencies with different link types.
556 adj_nbr_walk_nh6 (u32 sw_if_index,
557 const ip6_address_t *addr,
561 if (!ADJ_NBR_ITF_OK(FIB_PROTOCOL_IP6, sw_if_index))
564 ip46_address_t nh = {
568 adj_walk_nh_ctx_t awc = {
574 BV(clib_bihash_foreach_key_value_pair) (
575 adj_nbr_tables[FIB_PROTOCOL_IP6][sw_if_index],
581 * @brief Walk adjacencies on a link with a given next-hop.
582 * that is visit the adjacencies with different link types.
585 adj_nbr_walk_nh (u32 sw_if_index,
586 fib_protocol_t adj_nh_proto,
587 const ip46_address_t *nh,
591 if (!ADJ_NBR_ITF_OK(adj_nh_proto, sw_if_index))
594 adj_walk_nh_ctx_t awc = {
600 BV(clib_bihash_foreach_key_value_pair) (
601 adj_nbr_tables[adj_nh_proto][sw_if_index],
607 * Flags associated with the interface state walks
609 typedef enum adj_nbr_interface_flags_t_
611 ADJ_NBR_INTERFACE_UP = (1 << 0),
612 } adj_nbr_interface_flags_t;
615 * Context for the state change walk of the DB
617 typedef struct adj_nbr_interface_state_change_ctx_t_
620 * Flags on the interface
622 adj_nbr_interface_flags_t flags;
623 } adj_nbr_interface_state_change_ctx_t;
626 adj_nbr_interface_state_change_one (adj_index_t ai,
630 * Back walk the graph to inform the forwarding entries
631 * that this interface state has changed. Do this synchronously
632 * since this is the walk that provides convergence
634 adj_nbr_interface_state_change_ctx_t *ctx = arg;
636 fib_node_back_walk_ctx_t bw_ctx = {
637 .fnbw_reason = ((ctx->flags & ADJ_NBR_INTERFACE_UP) ?
638 FIB_NODE_BW_REASON_FLAG_INTERFACE_UP :
639 FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN),
641 * the force sync applies only as far as the first fib_entry.
642 * And it's the fib_entry's we need to converge away from
643 * the adjacencies on the now down link
645 .fnbw_flags = (!(ctx->flags & ADJ_NBR_INTERFACE_UP) ?
646 FIB_NODE_BW_FLAG_FORCE_SYNC :
650 fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &bw_ctx);
652 return (ADJ_WALK_RC_CONTINUE);
656 * @brief Registered function for SW interface state changes
658 static clib_error_t *
659 adj_nbr_sw_interface_state_change (vnet_main_t * vnm,
663 fib_protocol_t proto;
666 * walk each adj on the interface and trigger a walk from that adj
668 for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
670 adj_nbr_interface_state_change_ctx_t ctx = {
671 .flags = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
672 ADJ_NBR_INTERFACE_UP :
676 adj_nbr_walk(sw_if_index, proto,
677 adj_nbr_interface_state_change_one,
684 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION_PRIO(
685 adj_nbr_sw_interface_state_change,
686 VNET_ITF_FUNC_PRIORITY_HIGH);
689 * @brief Invoked on each SW interface of a HW interface when the
690 * HW interface state changes
693 adj_nbr_hw_sw_interface_state_change (vnet_main_t * vnm,
697 adj_nbr_interface_state_change_ctx_t *ctx = arg;
698 fib_protocol_t proto;
701 * walk each adj on the interface and trigger a walk from that adj
703 for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
705 adj_nbr_walk(sw_if_index, proto,
706 adj_nbr_interface_state_change_one,
712 * @brief Registered callback for HW interface state changes
714 static clib_error_t *
715 adj_nbr_hw_interface_state_change (vnet_main_t * vnm,
720 * walk SW interface on the HW
722 adj_nbr_interface_state_change_ctx_t ctx = {
723 .flags = ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) ?
724 ADJ_NBR_INTERFACE_UP :
728 vnet_hw_interface_walk_sw(vnm, hw_if_index,
729 adj_nbr_hw_sw_interface_state_change,
735 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION_PRIO(
736 adj_nbr_hw_interface_state_change,
737 VNET_ITF_FUNC_PRIORITY_HIGH);
740 adj_nbr_interface_delete_one (adj_index_t ai,
744 * Back walk the graph to inform the forwarding entries
745 * that this interface has been deleted.
747 fib_node_back_walk_ctx_t bw_ctx = {
748 .fnbw_reason = FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE,
751 fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &bw_ctx);
753 return (ADJ_WALK_RC_CONTINUE);
757 * adj_nbr_interface_add_del
759 * Registered to receive interface Add and delete notifications
761 static clib_error_t *
762 adj_nbr_interface_add_del (vnet_main_t * vnm,
766 fib_protocol_t proto;
771 * not interested in interface additions. we will not back walk
772 * to resolve paths through newly added interfaces. Why? The control
773 * plane should have the brains to add interfaces first, then routes.
774 * So the case where there are paths with a interface that matches
775 * one just created is the case where the path resolved through an
776 * interface that was deleted, and still has not been removed. The
777 * new interface added, is NO GUARANTEE that the interface being
778 * added now, even though it may have the same sw_if_index, is the
779 * same interface that the path needs. So tough!
780 * If the control plane wants these routes to resolve it needs to
781 * remove and add them again.
786 for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
788 adj_nbr_walk(sw_if_index, proto,
789 adj_nbr_interface_delete_one,
797 VNET_SW_INTERFACE_ADD_DEL_FUNCTION(adj_nbr_interface_add_del);
801 adj_nbr_show_one (adj_index_t ai,
804 vlib_cli_output (arg, "[@%d] %U",
806 format_ip_adjacency, ai,
807 FORMAT_IP_ADJACENCY_NONE);
809 return (ADJ_WALK_RC_CONTINUE);
812 static clib_error_t *
813 adj_nbr_show (vlib_main_t * vm,
814 unformat_input_t * input,
815 vlib_cli_command_t * cmd)
817 adj_index_t ai = ADJ_INDEX_INVALID;
818 u32 sw_if_index = ~0;
820 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
822 if (unformat (input, "%d", &ai))
824 else if (unformat (input, "%U",
825 unformat_vnet_sw_interface, vnet_get_main(),
832 if (ADJ_INDEX_INVALID != ai)
834 vlib_cli_output (vm, "[@%d] %U",
836 format_ip_adjacency, ai,
837 FORMAT_IP_ADJACENCY_DETAIL);
839 else if (~0 != sw_if_index)
841 fib_protocol_t proto;
843 for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
845 adj_nbr_walk(sw_if_index, proto,
852 fib_protocol_t proto;
854 for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
856 vec_foreach_index(sw_if_index, adj_nbr_tables[proto])
858 adj_nbr_walk(sw_if_index, proto,
869 * Show all neighbour adjacencies.
871 * @cliexstart{sh adj nbr}
872 * [@2] ipv4 via 1.0.0.2 loop0: IP4: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
873 * [@3] mpls via 1.0.0.2 loop0: MPLS_UNICAST: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
874 * [@4] ipv4 via 1.0.0.3 loop0: IP4: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
875 * [@5] mpls via 1.0.0.3 loop0: MPLS_UNICAST: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
878 VLIB_CLI_COMMAND (ip4_show_fib_command, static) = {
879 .path = "show adj nbr",
880 .short_help = "show adj nbr [<adj_index>] [interface]",
881 .function = adj_nbr_show,
885 adj_proto_to_46 (fib_protocol_t proto)
889 case FIB_PROTOCOL_IP4:
890 return (IP46_TYPE_IP4);
891 case FIB_PROTOCOL_IP6:
892 return (IP46_TYPE_IP6);
894 return (IP46_TYPE_IP4);
896 return (IP46_TYPE_IP4);
900 format_adj_nbr_incomplete (u8* s, va_list *ap)
902 index_t index = va_arg(*ap, index_t);
903 CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
904 vnet_main_t * vnm = vnet_get_main();
905 ip_adjacency_t * adj = adj_get(index);
907 s = format (s, "arp-%U", format_vnet_link, adj->ia_link);
908 s = format (s, ": via %U",
909 format_ip46_address, &adj->sub_type.nbr.next_hop,
910 adj_proto_to_46(adj->ia_nh_proto));
911 s = format (s, " %U",
912 format_vnet_sw_interface_name,
914 vnet_get_sw_interface(vnm,
915 adj->rewrite_header.sw_if_index));
921 format_adj_nbr (u8* s, va_list *ap)
923 index_t index = va_arg(*ap, index_t);
924 CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
925 vnet_main_t * vnm = vnet_get_main();
926 ip_adjacency_t * adj = adj_get(index);
928 s = format (s, "%U", format_vnet_link, adj->ia_link);
929 s = format (s, " via %U ",
930 format_ip46_address, &adj->sub_type.nbr.next_hop,
931 adj_proto_to_46(adj->ia_nh_proto));
934 vnm->vlib_main, &adj->rewrite_header, sizeof (adj->rewrite_data), 0);
940 adj_dpo_lock (dpo_id_t *dpo)
942 adj_lock(dpo->dpoi_index);
945 adj_dpo_unlock (dpo_id_t *dpo)
947 adj_unlock(dpo->dpoi_index);
953 fib_show_memory_usage("Adjacency",
956 sizeof(ip_adjacency_t));
959 const static dpo_vft_t adj_nbr_dpo_vft = {
960 .dv_lock = adj_dpo_lock,
961 .dv_unlock = adj_dpo_unlock,
962 .dv_format = format_adj_nbr,
963 .dv_mem_show = adj_mem_show,
965 const static dpo_vft_t adj_nbr_incompl_dpo_vft = {
966 .dv_lock = adj_dpo_lock,
967 .dv_unlock = adj_dpo_unlock,
968 .dv_format = format_adj_nbr_incomplete,
972 * @brief The per-protocol VLIB graph nodes that are assigned to an adjacency
975 * this means that these graph nodes are ones from which a nbr is the
976 * parent object in the DPO-graph.
978 const static char* const nbr_ip4_nodes[] =
980 "ip4-rewrite-transit",
983 const static char* const nbr_ip6_nodes[] =
988 const static char* const nbr_mpls_nodes[] =
993 const static char* const nbr_ethernet_nodes[] =
998 const static char* const * const nbr_nodes[DPO_PROTO_NUM] =
1000 [DPO_PROTO_IP4] = nbr_ip4_nodes,
1001 [DPO_PROTO_IP6] = nbr_ip6_nodes,
1002 [DPO_PROTO_MPLS] = nbr_mpls_nodes,
1003 [DPO_PROTO_ETHERNET] = nbr_ethernet_nodes,
1006 const static char* const nbr_incomplete_ip4_nodes[] =
1011 const static char* const nbr_incomplete_ip6_nodes[] =
1013 "ip6-discover-neighbor",
1016 const static char* const nbr_incomplete_mpls_nodes[] =
1018 "mpls-adj-incomplete",
1022 const static char* const * const nbr_incomplete_nodes[DPO_PROTO_NUM] =
1024 [DPO_PROTO_IP4] = nbr_incomplete_ip4_nodes,
1025 [DPO_PROTO_IP6] = nbr_incomplete_ip6_nodes,
1026 [DPO_PROTO_MPLS] = nbr_incomplete_mpls_nodes,
1030 adj_nbr_module_init (void)
1032 dpo_register(DPO_ADJACENCY,
1035 dpo_register(DPO_ADJACENCY_INCOMPLETE,
1036 &adj_nbr_incompl_dpo_vft,
1037 nbr_incomplete_nodes);