1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Zebra next hop tracking code
3 * Copyright (C) 2013 Cumulus Networks, Inc.
14 #include "sockunion.h"
17 #include "workqueue.h"
24 #include "zebra/zebra_router.h"
25 #include "zebra/rib.h"
27 #include "zebra/zserv.h"
28 #include "zebra/zebra_ns.h"
29 #include "zebra/zebra_vrf.h"
30 #include "zebra/redistribute.h"
31 #include "zebra/debug.h"
32 #include "zebra/zebra_rnh.h"
33 #include "zebra/zebra_routemap.h"
34 #include "zebra/zebra_srte.h"
35 #include "zebra/interface.h"
36 #include "zebra/zebra_errors.h"
38 DEFINE_MTYPE_STATIC(ZEBRA
, RNH
, "Nexthop tracking object");
40 /* UI controls whether to notify about changes that only involve backup
41 * nexthops. Default is to notify all changes.
43 static bool rnh_hide_backups
;
45 static void free_state(vrf_id_t vrf_id
, struct route_entry
*re
,
46 struct route_node
*rn
);
47 static void copy_state(struct rnh
*rnh
, const struct route_entry
*re
,
48 struct route_node
*rn
);
49 static bool compare_state(struct route_entry
*r1
, struct route_entry
*r2
);
50 static void print_rnh(struct route_node
*rn
, struct vty
*vty
,
52 static int zebra_client_cleanup_rnh(struct zserv
*client
);
54 void zebra_rnh_init(void)
56 hook_register(zserv_client_close
, zebra_client_cleanup_rnh
);
59 static inline struct route_table
*get_rnh_table(vrf_id_t vrfid
, afi_t afi
,
62 struct zebra_vrf
*zvrf
;
63 struct route_table
*t
= NULL
;
65 zvrf
= zebra_vrf_lookup_by_id(vrfid
);
67 if (safi
== SAFI_UNICAST
)
68 t
= zvrf
->rnh_table
[afi
];
69 else if (safi
== SAFI_MULTICAST
)
70 t
= zvrf
->rnh_table_multicast
[afi
];
76 static void zebra_rnh_remove_from_routing_table(struct rnh
*rnh
)
78 struct zebra_vrf
*zvrf
= zebra_vrf_lookup_by_id(rnh
->vrf_id
);
79 struct route_table
*table
= zvrf
->table
[rnh
->afi
][rnh
->safi
];
80 struct route_node
*rn
;
86 rn
= route_node_match(table
, &rnh
->resolved_route
);
90 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
91 zlog_debug("%s: %s(%u):%pRN removed from tracking on %pRN",
92 __func__
, VRF_LOGNAME(zvrf
->vrf
), rnh
->vrf_id
,
95 dest
= rib_dest_from_rnode(rn
);
96 rnh_list_del(&dest
->nht
, rnh
);
97 route_unlock_node(rn
);
100 static void zebra_rnh_store_in_routing_table(struct rnh
*rnh
)
102 struct zebra_vrf
*zvrf
= zebra_vrf_lookup_by_id(rnh
->vrf_id
);
103 struct route_table
*table
= zvrf
->table
[rnh
->afi
][rnh
->safi
];
104 struct route_node
*rn
;
107 rn
= route_node_match(table
, &rnh
->resolved_route
);
111 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
112 zlog_debug("%s: %s(%u):%pRN added for tracking on %pRN",
113 __func__
, VRF_LOGNAME(zvrf
->vrf
), rnh
->vrf_id
,
116 dest
= rib_dest_from_rnode(rn
);
117 rnh_list_add_tail(&dest
->nht
, rnh
);
118 route_unlock_node(rn
);
121 struct rnh
*zebra_add_rnh(struct prefix
*p
, vrf_id_t vrfid
, safi_t safi
,
124 struct route_table
*table
;
125 struct route_node
*rn
;
126 struct rnh
*rnh
= NULL
;
127 afi_t afi
= family2afi(p
->family
);
129 if (IS_ZEBRA_DEBUG_NHT
) {
130 struct vrf
*vrf
= vrf_lookup_by_id(vrfid
);
132 zlog_debug("%s(%u): Add RNH %pFX for safi: %u",
133 VRF_LOGNAME(vrf
), vrfid
, p
, safi
);
136 table
= get_rnh_table(vrfid
, afi
, safi
);
138 struct vrf
*vrf
= vrf_lookup_by_id(vrfid
);
140 flog_warn(EC_ZEBRA_RNH_NO_TABLE
,
141 "%s(%u): Add RNH %pFX - table not found",
142 VRF_LOGNAME(vrf
), vrfid
, p
);
147 /* Make it sure prefixlen is applied to the prefix. */
150 /* Lookup (or add) route node.*/
151 rn
= route_node_get(table
, p
);
154 rnh
= XCALLOC(MTYPE_RNH
, sizeof(struct rnh
));
157 * The resolved route is already 0.0.0.0/0 or
158 * 0::0/0 due to the calloc right above, but
159 * we should set the family so that future
160 * comparisons can just be done
162 rnh
->resolved_route
.family
= p
->family
;
163 rnh
->client_list
= list_new();
168 rnh
->zebra_pseudowire_list
= list_new();
174 zebra_rnh_store_in_routing_table(rnh
);
178 route_unlock_node(rn
);
182 struct rnh
*zebra_lookup_rnh(struct prefix
*p
, vrf_id_t vrfid
, safi_t safi
)
184 struct route_table
*table
;
185 struct route_node
*rn
;
187 table
= get_rnh_table(vrfid
, family2afi(PREFIX_FAMILY(p
)), safi
);
191 /* Make it sure prefixlen is applied to the prefix. */
194 /* Lookup route node.*/
195 rn
= route_node_lookup(table
, p
);
199 route_unlock_node(rn
);
203 void zebra_free_rnh(struct rnh
*rnh
)
205 struct zebra_vrf
*zvrf
;
206 struct route_table
*table
;
208 zebra_rnh_remove_from_routing_table(rnh
);
209 rnh
->flags
|= ZEBRA_NHT_DELETED
;
210 list_delete(&rnh
->client_list
);
211 list_delete(&rnh
->zebra_pseudowire_list
);
213 zvrf
= zebra_vrf_lookup_by_id(rnh
->vrf_id
);
214 table
= zvrf
->table
[family2afi(rnh
->resolved_route
.family
)][rnh
->safi
];
217 struct route_node
*rern
;
219 rern
= route_node_match(table
, &rnh
->resolved_route
);
223 route_unlock_node(rern
);
225 dest
= rib_dest_from_rnode(rern
);
226 rnh_list_del(&dest
->nht
, rnh
);
229 free_state(rnh
->vrf_id
, rnh
->state
, rnh
->node
);
230 XFREE(MTYPE_RNH
, rnh
);
233 static void zebra_delete_rnh(struct rnh
*rnh
)
235 struct route_node
*rn
;
237 if (!list_isempty(rnh
->client_list
)
238 || !list_isempty(rnh
->zebra_pseudowire_list
))
241 if ((rnh
->flags
& ZEBRA_NHT_DELETED
) || !(rn
= rnh
->node
))
244 if (IS_ZEBRA_DEBUG_NHT
) {
245 struct vrf
*vrf
= vrf_lookup_by_id(rnh
->vrf_id
);
247 zlog_debug("%s(%u): Del RNH %pRN", VRF_LOGNAME(vrf
),
248 rnh
->vrf_id
, rnh
->node
);
253 route_unlock_node(rn
);
257 * This code will send to the registering client
259 * For a rnh that was created, there is no data
260 * so it will send an empty nexthop group
261 * If rnh exists then we know it has been evaluated
262 * and as such it will have a resolved rnh.
264 void zebra_add_rnh_client(struct rnh
*rnh
, struct zserv
*client
,
267 if (IS_ZEBRA_DEBUG_NHT
) {
268 struct vrf
*vrf
= vrf_lookup_by_id(vrf_id
);
270 zlog_debug("%s(%u): Client %s registers for RNH %pRN",
271 VRF_LOGNAME(vrf
), vrf_id
,
272 zebra_route_string(client
->proto
), rnh
->node
);
274 if (!listnode_lookup(rnh
->client_list
, client
))
275 listnode_add(rnh
->client_list
, client
);
278 * We always need to respond with known information,
279 * currently multiple daemons expect this behavior
281 zebra_send_rnh_update(rnh
, client
, vrf_id
, 0);
284 void zebra_remove_rnh_client(struct rnh
*rnh
, struct zserv
*client
)
286 if (IS_ZEBRA_DEBUG_NHT
) {
287 struct vrf
*vrf
= vrf_lookup_by_id(rnh
->vrf_id
);
289 zlog_debug("Client %s unregisters for RNH %s(%u)%pRN",
290 zebra_route_string(client
->proto
), VRF_LOGNAME(vrf
),
291 vrf
->vrf_id
, rnh
->node
);
293 listnode_delete(rnh
->client_list
, client
);
294 zebra_delete_rnh(rnh
);
297 /* XXX move this utility function elsewhere? */
298 static void addr2hostprefix(int af
, const union g_addr
*addr
,
299 struct prefix
*prefix
)
303 prefix
->family
= AF_INET
;
304 prefix
->prefixlen
= IPV4_MAX_BITLEN
;
305 prefix
->u
.prefix4
= addr
->ipv4
;
308 prefix
->family
= AF_INET6
;
309 prefix
->prefixlen
= IPV6_MAX_BITLEN
;
310 prefix
->u
.prefix6
= addr
->ipv6
;
313 memset(prefix
, 0, sizeof(*prefix
));
314 zlog_warn("%s: unknown address family %d", __func__
, af
);
319 void zebra_register_rnh_pseudowire(vrf_id_t vrf_id
, struct zebra_pw
*pw
,
325 struct zebra_vrf
*zvrf
;
329 zvrf
= zebra_vrf_lookup_by_id(vrf_id
);
333 addr2hostprefix(pw
->af
, &pw
->nexthop
, &nh
);
334 rnh
= zebra_add_rnh(&nh
, vrf_id
, SAFI_UNICAST
, &exists
);
338 if (!listnode_lookup(rnh
->zebra_pseudowire_list
, pw
)) {
339 listnode_add(rnh
->zebra_pseudowire_list
, pw
);
341 zebra_evaluate_rnh(zvrf
, family2afi(pw
->af
), 1, &nh
,
347 void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id
, struct zebra_pw
*pw
)
355 listnode_delete(rnh
->zebra_pseudowire_list
, pw
);
358 zebra_delete_rnh(rnh
);
361 /* Clear the NEXTHOP_FLAG_RNH_FILTERED flags on all nexthops
363 static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry
*re
)
365 struct nexthop
*nexthop
;
368 for (nexthop
= re
->nhe
->nhg
.nexthop
; nexthop
;
369 nexthop
= nexthop
->next
) {
370 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_RNH_FILTERED
);
375 /* Apply the NHT route-map for a client to the route (and nexthops)
378 static int zebra_rnh_apply_nht_rmap(afi_t afi
, struct zebra_vrf
*zvrf
,
379 struct route_node
*prn
,
380 struct route_entry
*re
, int proto
)
382 int at_least_one
= 0;
383 struct nexthop
*nexthop
;
384 route_map_result_t ret
;
387 for (nexthop
= re
->nhe
->nhg
.nexthop
; nexthop
;
388 nexthop
= nexthop
->next
) {
389 ret
= zebra_nht_route_map_check(
390 afi
, proto
, &prn
->p
, zvrf
, re
, nexthop
);
391 if (ret
!= RMAP_DENYMATCH
)
392 at_least_one
++; /* at least one valid NH */
394 SET_FLAG(nexthop
->flags
,
395 NEXTHOP_FLAG_RNH_FILTERED
);
399 return (at_least_one
);
403 * Notify clients registered for this nexthop about a change.
405 static void zebra_rnh_notify_protocol_clients(struct zebra_vrf
*zvrf
, afi_t afi
,
406 struct route_node
*nrn
,
408 struct route_node
*prn
,
409 struct route_entry
*re
)
411 struct listnode
*node
;
412 struct zserv
*client
;
413 int num_resolving_nh
;
415 if (IS_ZEBRA_DEBUG_NHT
) {
417 zlog_debug("%s(%u):%pRN: NH resolved over route %pRN",
418 VRF_LOGNAME(zvrf
->vrf
), zvrf
->vrf
->vrf_id
,
421 zlog_debug("%s(%u):%pRN: NH has become unresolved",
422 VRF_LOGNAME(zvrf
->vrf
), zvrf
->vrf
->vrf_id
,
426 for (ALL_LIST_ELEMENTS_RO(rnh
->client_list
, node
, client
)) {
428 /* Apply route-map for this client to route resolving
430 * nexthop to see if it is filtered or not.
432 zebra_rnh_clear_nexthop_rnh_filters(re
);
433 num_resolving_nh
= zebra_rnh_apply_nht_rmap(
434 afi
, zvrf
, prn
, re
, client
->proto
);
435 if (num_resolving_nh
)
436 rnh
->filtered
[client
->proto
] = 0;
438 rnh
->filtered
[client
->proto
] = 1;
440 if (IS_ZEBRA_DEBUG_NHT
)
442 "%s(%u):%pRN: Notifying client %s about NH %s",
443 VRF_LOGNAME(zvrf
->vrf
),
444 zvrf
->vrf
->vrf_id
, nrn
,
445 zebra_route_string(client
->proto
),
448 : "(filtered by route-map)");
450 rnh
->filtered
[client
->proto
] = 0;
451 if (IS_ZEBRA_DEBUG_NHT
)
453 "%s(%u):%pRN: Notifying client %s about NH (unreachable)",
454 VRF_LOGNAME(zvrf
->vrf
),
455 zvrf
->vrf
->vrf_id
, nrn
,
456 zebra_route_string(client
->proto
));
459 zebra_send_rnh_update(rnh
, client
, zvrf
->vrf
->vrf_id
, 0);
463 zebra_rnh_clear_nexthop_rnh_filters(re
);
467 * Utility to determine whether a candidate nexthop is useable. We make this
468 * check in a couple of places, so this is a single home for the logic we
472 static const int RNH_INVALID_NH_FLAGS
= (NEXTHOP_FLAG_RECURSIVE
|
473 NEXTHOP_FLAG_DUPLICATE
|
474 NEXTHOP_FLAG_RNH_FILTERED
);
476 bool rnh_nexthop_valid(const struct route_entry
*re
, const struct nexthop
*nh
)
478 return (CHECK_FLAG(re
->status
, ROUTE_ENTRY_INSTALLED
)
479 && CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_ACTIVE
)
480 && !CHECK_FLAG(nh
->flags
, RNH_INVALID_NH_FLAGS
));
484 * Determine whether an re's nexthops are valid for tracking.
486 static bool rnh_check_re_nexthops(const struct route_entry
*re
,
487 const struct rnh
*rnh
)
490 const struct nexthop
*nexthop
= NULL
;
492 /* Check route's nexthops */
493 for (ALL_NEXTHOPS(re
->nhe
->nhg
, nexthop
)) {
494 if (rnh_nexthop_valid(re
, nexthop
))
498 /* Check backup nexthops, if any. */
499 if (nexthop
== NULL
&& re
->nhe
->backup_info
&&
500 re
->nhe
->backup_info
->nhe
) {
501 for (ALL_NEXTHOPS(re
->nhe
->backup_info
->nhe
->nhg
, nexthop
)) {
502 if (rnh_nexthop_valid(re
, nexthop
))
507 if (nexthop
== NULL
) {
508 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
510 " Route Entry %s no nexthops",
511 zebra_route_string(re
->type
));
516 /* Some special checks if registration asked for them. */
517 if (CHECK_FLAG(rnh
->flags
, ZEBRA_NHT_CONNECTED
)) {
518 if ((re
->type
== ZEBRA_ROUTE_CONNECT
)
519 || (re
->type
== ZEBRA_ROUTE_STATIC
))
521 if (re
->type
== ZEBRA_ROUTE_NHRP
) {
523 for (nexthop
= re
->nhe
->nhg
.nexthop
;
525 nexthop
= nexthop
->next
)
526 if (nexthop
->type
== NEXTHOP_TYPE_IFINDEX
)
540 * Determine appropriate route (route entry) resolving a tracked
543 static struct route_entry
*
544 zebra_rnh_resolve_nexthop_entry(struct zebra_vrf
*zvrf
, afi_t afi
,
545 struct route_node
*nrn
, const struct rnh
*rnh
,
546 struct route_node
**prn
)
548 struct route_table
*route_table
;
549 struct route_node
*rn
;
550 struct route_entry
*re
;
554 route_table
= zvrf
->table
[afi
][rnh
->safi
];
558 rn
= route_node_match(route_table
, &nrn
->p
);
562 /* Unlock route node - we don't need to lock when walking the tree. */
563 route_unlock_node(rn
);
565 /* While resolving nexthops, we may need to walk up the tree from the
566 * most-specific match. Do similar logic as in zebra_rib.c
569 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
570 zlog_debug("%s: %s(%u):%pRN Possible Match to %pRN",
571 __func__
, VRF_LOGNAME(zvrf
->vrf
),
572 rnh
->vrf_id
, rnh
->node
, rn
);
574 /* Do not resolve over default route unless allowed &&
575 * match route to be exact if so specified
577 if (is_default_prefix(&rn
->p
)
578 && (!CHECK_FLAG(rnh
->flags
, ZEBRA_NHT_RESOLVE_VIA_DEFAULT
)
579 && !rnh_resolve_via_default(zvrf
, rn
->p
.family
))) {
580 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
582 " Not allowed to resolve through default prefix: rnh->resolve_via_default: %u",
585 ZEBRA_NHT_RESOLVE_VIA_DEFAULT
));
589 /* Identify appropriate route entry. */
590 RNODE_FOREACH_RE (rn
, re
) {
591 if (CHECK_FLAG(re
->status
, ROUTE_ENTRY_REMOVED
)) {
592 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
594 " Route Entry %s removed",
595 zebra_route_string(re
->type
));
598 if (!CHECK_FLAG(re
->flags
, ZEBRA_FLAG_SELECTED
) &&
599 !CHECK_FLAG(re
->flags
, ZEBRA_FLAG_FIB_OVERRIDE
)) {
600 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
602 " Route Entry %s !selected",
603 zebra_route_string(re
->type
));
607 if (CHECK_FLAG(re
->status
, ROUTE_ENTRY_QUEUED
)) {
608 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
610 " Route Entry %s queued",
611 zebra_route_string(re
->type
));
615 /* Just being SELECTED isn't quite enough - must
616 * have an installed nexthop to be useful.
618 if (rnh_check_re_nexthops(re
, rnh
))
622 /* Route entry found, we're done; else, walk up the tree. */
627 /* Resolve the nexthop recursively by finding matching
628 * route with lower prefix length
637 static void zebra_rnh_process_pseudowires(vrf_id_t vrfid
, struct rnh
*rnh
)
640 struct listnode
*node
;
642 for (ALL_LIST_ELEMENTS_RO(rnh
->zebra_pseudowire_list
, node
, pw
))
647 * See if a tracked nexthop entry has undergone any change, and if so,
648 * take appropriate action; this involves notifying any clients and/or
649 * scheduling dependent static routes for processing.
651 static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf
*zvrf
, afi_t afi
,
652 int force
, struct route_node
*nrn
,
654 struct route_node
*prn
,
655 struct route_entry
*re
)
657 int state_changed
= 0;
659 /* If we're resolving over a different route, resolution has changed or
660 * the resolving route has some change (e.g., metric), there is a state
663 zebra_rnh_remove_from_routing_table(rnh
);
664 if (!prefix_same(&rnh
->resolved_route
, prn
? &prn
->p
: NULL
)) {
666 prefix_copy(&rnh
->resolved_route
, &prn
->p
);
669 * Just quickly store the family of the resolved
670 * route so that we can reset it in a second here
672 int family
= rnh
->resolved_route
.family
;
674 memset(&rnh
->resolved_route
, 0, sizeof(struct prefix
));
675 rnh
->resolved_route
.family
= family
;
678 copy_state(rnh
, re
, nrn
);
680 } else if (compare_state(re
, rnh
->state
)) {
681 copy_state(rnh
, re
, nrn
);
684 zebra_rnh_store_in_routing_table(rnh
);
686 if (state_changed
|| force
) {
687 /* NOTE: Use the "copy" of resolving route stored in 'rnh' i.e.,
690 /* Notify registered protocol clients. */
691 zebra_rnh_notify_protocol_clients(zvrf
, afi
, nrn
, rnh
, prn
,
694 /* Process pseudowires attached to this nexthop */
695 zebra_rnh_process_pseudowires(zvrf
->vrf
->vrf_id
, rnh
);
699 /* Evaluate one tracked entry */
700 static void zebra_rnh_evaluate_entry(struct zebra_vrf
*zvrf
, afi_t afi
,
701 int force
, struct route_node
*nrn
)
704 struct route_entry
*re
;
705 struct route_node
*prn
;
707 if (IS_ZEBRA_DEBUG_NHT
) {
708 zlog_debug("%s(%u):%pRN: Evaluate RNH, %s",
709 VRF_LOGNAME(zvrf
->vrf
), zvrf
->vrf
->vrf_id
, nrn
,
710 force
? "(force)" : "");
715 /* Identify route entry (RE) resolving this tracked entry. */
716 re
= zebra_rnh_resolve_nexthop_entry(zvrf
, afi
, nrn
, rnh
, &prn
);
718 /* If the entry cannot be resolved and that is also the existing state,
719 * there is nothing further to do.
721 if (!re
&& rnh
->state
== NULL
&& !force
)
724 /* Process based on type of entry. */
725 zebra_rnh_eval_nexthop_entry(zvrf
, afi
, force
, nrn
, rnh
, prn
, re
);
729 * Clear the ROUTE_ENTRY_NEXTHOPS_CHANGED flag
730 * from the re entries.
732 * Please note we are doing this *after* we have
733 * notified the world about each nexthop as that
734 * we can have a situation where one re entry
735 * covers multiple nexthops we are interested in.
737 static void zebra_rnh_clear_nhc_flag(struct zebra_vrf
*zvrf
, afi_t afi
,
738 struct route_node
*nrn
)
741 struct route_entry
*re
;
742 struct route_node
*prn
;
746 /* Identify route entry (RIB) resolving this tracked entry. */
747 re
= zebra_rnh_resolve_nexthop_entry(zvrf
, afi
, nrn
, rnh
, &prn
);
750 UNSET_FLAG(re
->status
, ROUTE_ENTRY_LABELS_CHANGED
);
753 /* Evaluate all tracked entries (nexthops or routes for import into BGP)
754 * of a particular VRF and address-family or a specific prefix.
756 void zebra_evaluate_rnh(struct zebra_vrf
*zvrf
, afi_t afi
, int force
,
757 const struct prefix
*p
, safi_t safi
)
759 struct route_table
*rnh_table
;
760 struct route_node
*nrn
;
762 rnh_table
= get_rnh_table(zvrf
->vrf
->vrf_id
, afi
, safi
);
763 if (!rnh_table
) // unexpected
767 /* Evaluating a specific entry, make sure it exists. */
768 nrn
= route_node_lookup(rnh_table
, p
);
769 if (nrn
&& nrn
->info
)
770 zebra_rnh_evaluate_entry(zvrf
, afi
, force
, nrn
);
773 route_unlock_node(nrn
);
775 /* Evaluate entire table. */
776 nrn
= route_top(rnh_table
);
779 zebra_rnh_evaluate_entry(zvrf
, afi
, force
, nrn
);
780 nrn
= route_next(nrn
); /* this will also unlock nrn */
782 nrn
= route_top(rnh_table
);
785 zebra_rnh_clear_nhc_flag(zvrf
, afi
, nrn
);
786 nrn
= route_next(nrn
); /* this will also unlock nrn */
791 void zebra_print_rnh_table(vrf_id_t vrfid
, afi_t afi
, safi_t safi
,
792 struct vty
*vty
, const struct prefix
*p
,
795 struct route_table
*table
;
796 struct route_node
*rn
;
798 table
= get_rnh_table(vrfid
, afi
, safi
);
800 if (IS_ZEBRA_DEBUG_NHT
)
801 zlog_debug("print_rnhs: rnh table not found");
805 for (rn
= route_top(table
); rn
; rn
= route_next(rn
)) {
806 if (p
&& !prefix_match(&rn
->p
, p
))
810 print_rnh(rn
, vty
, json
);
815 * free_state - free up the re structure associated with the rnh.
817 static void free_state(vrf_id_t vrf_id
, struct route_entry
*re
,
818 struct route_node
*rn
)
823 /* free RE and nexthops */
824 zebra_nhg_free(re
->nhe
);
828 static void copy_state(struct rnh
*rnh
, const struct route_entry
*re
,
829 struct route_node
*rn
)
831 struct route_entry
*state
;
834 free_state(rnh
->vrf_id
, rnh
->state
, rn
);
841 state
= XCALLOC(MTYPE_RE
, sizeof(struct route_entry
));
842 state
->type
= re
->type
;
843 state
->distance
= re
->distance
;
844 state
->metric
= re
->metric
;
845 state
->vrf_id
= re
->vrf_id
;
846 state
->status
= re
->status
;
848 state
->nhe
= zebra_nhe_copy(re
->nhe
, 0);
850 /* Copy the 'fib' nexthops also, if present - we want to capture
851 * the true installed nexthops.
853 if (re
->fib_ng
.nexthop
)
854 nexthop_group_copy(&state
->fib_ng
, &re
->fib_ng
);
855 if (re
->fib_backup_ng
.nexthop
)
856 nexthop_group_copy(&state
->fib_backup_ng
, &re
->fib_backup_ng
);
862 * Locate the next primary nexthop, used when comparing current rnh info with
865 static struct nexthop
*next_valid_primary_nh(struct route_entry
*re
,
868 struct nexthop_group
*nhg
;
871 bool default_path
= true;
873 /* Fib backup ng present: some backups are installed,
874 * and we're configured for special handling if there are backups.
876 if (rnh_hide_backups
&& (re
->fib_backup_ng
.nexthop
!= NULL
))
877 default_path
= false;
879 /* Default path: no special handling, just using the 'installed'
880 * primary nexthops and the common validity test.
884 nhg
= rib_get_fib_nhg(re
);
887 nh
= nexthop_next(nh
);
890 if (rnh_nexthop_valid(re
, nh
))
893 nh
= nexthop_next(nh
);
899 /* Hide backup activation/switchover events.
901 * If we've had a switchover, an inactive primary won't be in
902 * the fib list at all - the 'fib' list could even be empty
903 * in the case where no primary is installed. But we want to consider
904 * those primaries "valid" if they have an activated backup nh.
906 * The logic is something like:
908 * // then all primaries are installed
910 * for each primary in re nhg
912 * primary is installed
913 * else if a backup is installed
914 * primary counts as installed
919 /* Start with the first primary */
921 nh
= re
->nhe
->nhg
.nexthop
;
923 nh
= nexthop_next(nh
);
927 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
928 zlog_debug("%s: checking primary NH %pNHv",
931 /* If this nexthop is in the fib list, it's installed */
932 nhg
= rib_get_fib_nhg(re
);
934 for (bnh
= nhg
->nexthop
; bnh
; bnh
= nexthop_next(bnh
)) {
935 if (nexthop_cmp(nh
, bnh
) == 0)
940 /* Found the match */
941 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
942 zlog_debug("%s: NH in fib list", __func__
);
946 /* Else if this nexthop's backup is installed, it counts */
947 nhg
= rib_get_fib_backup_nhg(re
);
950 for (idx
= 0; bnh
!= NULL
; idx
++) {
951 /* If we find an active backup nh for this
952 * primary, we're done;
954 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
955 zlog_debug("%s: checking backup %pNHv [%d]",
958 if (!CHECK_FLAG(bnh
->flags
, NEXTHOP_FLAG_ACTIVE
))
961 for (i
= 0; i
< nh
->backup_num
; i
++) {
962 /* Found a matching activated backup nh */
963 if (nh
->backup_idx
[i
] == idx
) {
964 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
965 zlog_debug("%s: backup %d activated",
972 /* Note that we're not recursing here if the
973 * backups are recursive: the primary's index is
974 * only valid in the top-level backup list.
979 /* Try the next primary nexthop */
980 nh
= nexthop_next(nh
);
989 * Compare two route_entries' nexthops. Account for backup nexthops
990 * and for the 'fib' nexthop lists, if present.
992 static bool compare_valid_nexthops(struct route_entry
*r1
,
993 struct route_entry
*r2
)
995 bool matched_p
= false;
996 struct nexthop_group
*nhg1
, *nhg2
;
997 struct nexthop
*nh1
, *nh2
;
999 /* Start with the primary nexthops */
1001 nh1
= next_valid_primary_nh(r1
, NULL
);
1002 nh2
= next_valid_primary_nh(r2
, NULL
);
1005 /* Find any differences in the nexthop lists */
1008 /* Any difference is a no-match */
1009 if (nexthop_cmp(nh1
, nh2
) != 0) {
1010 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
1011 zlog_debug("%s: nh1: %pNHv, nh2: %pNHv differ",
1012 __func__
, nh1
, nh2
);
1016 } else if (nh1
|| nh2
) {
1017 /* One list has more valid nexthops than the other */
1018 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
1019 zlog_debug("%s: nh1 %s, nh2 %s", __func__
,
1020 nh1
? "non-NULL" : "NULL",
1021 nh2
? "non-NULL" : "NULL");
1024 break; /* Done with both lists */
1026 nh1
= next_valid_primary_nh(r1
, nh1
);
1027 nh2
= next_valid_primary_nh(r2
, nh2
);
1030 /* If configured, don't compare installed backup state - we've
1031 * accounted for that with the primaries above.
1033 * But we do want to compare the routes' backup info,
1034 * in case the owning route has changed the backups -
1035 * that change we do want to report.
1037 if (rnh_hide_backups
) {
1038 uint32_t hash1
= 0, hash2
= 0;
1040 if (r1
->nhe
->backup_info
)
1041 hash1
= nexthop_group_hash(
1042 &r1
->nhe
->backup_info
->nhe
->nhg
);
1044 if (r2
->nhe
->backup_info
)
1045 hash2
= nexthop_group_hash(
1046 &r2
->nhe
->backup_info
->nhe
->nhg
);
1048 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
1049 zlog_debug("%s: backup hash1 %#x, hash2 %#x",
1050 __func__
, hash1
, hash2
);
1058 /* The test for the backups is slightly different: the only installed
1059 * backups will be in the 'fib' list.
1061 nhg1
= rib_get_fib_backup_nhg(r1
);
1062 nhg2
= rib_get_fib_backup_nhg(r2
);
1064 nh1
= nhg1
->nexthop
;
1065 nh2
= nhg2
->nexthop
;
1068 /* Find each backup list's next valid nexthop */
1069 while ((nh1
!= NULL
) && !rnh_nexthop_valid(r1
, nh1
))
1070 nh1
= nexthop_next(nh1
);
1072 while ((nh2
!= NULL
) && !rnh_nexthop_valid(r2
, nh2
))
1073 nh2
= nexthop_next(nh2
);
1076 /* Any difference is a no-match */
1077 if (nexthop_cmp(nh1
, nh2
) != 0) {
1078 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
1079 zlog_debug("%s: backup nh1: %pNHv, nh2: %pNHv differ",
1080 __func__
, nh1
, nh2
);
1084 nh1
= nexthop_next(nh1
);
1085 nh2
= nexthop_next(nh2
);
1086 } else if (nh1
|| nh2
) {
1087 /* One list has more valid nexthops than the other */
1088 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
1089 zlog_debug("%s: backup nh1 %s, nh2 %s",
1091 nh1
? "non-NULL" : "NULL",
1092 nh2
? "non-NULL" : "NULL");
1095 break; /* Done with both lists */
1100 /* Well, it's a match */
1105 if (IS_ZEBRA_DEBUG_NHT_DETAILED
)
1106 zlog_debug("%s: %smatched",
1107 __func__
, (matched_p
? "" : "NOT "));
1112 /* Returns 'false' if no difference. */
1113 static bool compare_state(struct route_entry
*r1
,
1114 struct route_entry
*r2
)
1119 if ((!r1
&& r2
) || (r1
&& !r2
))
1122 if (r1
->distance
!= r2
->distance
)
1125 if (r1
->metric
!= r2
->metric
)
1128 if (!compare_valid_nexthops(r1
, r2
))
1134 int zebra_send_rnh_update(struct rnh
*rnh
, struct zserv
*client
,
1135 vrf_id_t vrf_id
, uint32_t srte_color
)
1137 struct stream
*s
= NULL
;
1138 struct route_entry
*re
;
1142 struct route_node
*rn
;
1144 uint32_t message
= 0;
1149 /* Get output stream. */
1150 s
= stream_new(ZEBRA_MAX_PACKET_SIZ
);
1152 zclient_create_header(s
, ZEBRA_NEXTHOP_UPDATE
, vrf_id
);
1154 /* Message flags. */
1156 SET_FLAG(message
, ZAPI_MESSAGE_SRTE
);
1157 stream_putl(s
, message
);
1160 * Put what we were told to match against
1162 stream_putw(s
, rnh
->safi
);
1163 stream_putw(s
, rn
->p
.family
);
1164 stream_putc(s
, rn
->p
.prefixlen
);
1165 switch (rn
->p
.family
) {
1167 stream_put_in_addr(s
, &rn
->p
.u
.prefix4
);
1170 stream_put(s
, &rn
->p
.u
.prefix6
, IPV6_MAX_BYTELEN
);
1173 flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY
,
1174 "%s: Unknown family (%d) notification attempted",
1175 __func__
, rn
->p
.family
);
1180 * What we matched against
1182 stream_putw(s
, rnh
->resolved_route
.family
);
1183 stream_putc(s
, rnh
->resolved_route
.prefixlen
);
1184 switch (rnh
->resolved_route
.family
) {
1186 stream_put_in_addr(s
, &rnh
->resolved_route
.u
.prefix4
);
1189 stream_put(s
, &rnh
->resolved_route
.u
.prefix6
, IPV6_MAX_BYTELEN
);
1192 flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY
,
1193 "%s: Unknown family (%d) notification attempted",
1194 __func__
, rn
->p
.family
);
1199 stream_putl(s
, srte_color
);
1202 struct zapi_nexthop znh
;
1203 struct nexthop_group
*nhg
;
1205 stream_putc(s
, re
->type
);
1206 stream_putw(s
, re
->instance
);
1207 stream_putc(s
, re
->distance
);
1208 stream_putl(s
, re
->metric
);
1210 nump
= stream_get_endp(s
);
1213 nhg
= rib_get_fib_nhg(re
);
1214 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
1215 if (rnh_nexthop_valid(re
, nh
)) {
1216 zapi_nexthop_from_nexthop(&znh
, nh
);
1217 ret
= zapi_nexthop_encode(s
, &znh
, 0, message
);
1224 nhg
= rib_get_fib_backup_nhg(re
);
1226 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
1227 if (rnh_nexthop_valid(re
, nh
)) {
1228 zapi_nexthop_from_nexthop(&znh
, nh
);
1229 ret
= zapi_nexthop_encode(
1230 s
, &znh
, 0 /* flags */,
1239 stream_putc_at(s
, nump
, num
);
1241 stream_putc(s
, 0); // type
1242 stream_putw(s
, 0); // instance
1243 stream_putc(s
, 0); // distance
1244 stream_putl(s
, 0); // metric
1245 stream_putc(s
, 0); // nexthops
1247 stream_putw_at(s
, 0, stream_get_endp(s
));
1249 client
->nh_last_upd_time
= monotime(NULL
);
1250 return zserv_send_message(client
, s
);
1260 * Render a nexthop into a json object; the caller allocates and owns
1261 * the json object memory.
1263 void show_nexthop_json_helper(json_object
*json_nexthop
,
1264 const struct nexthop
*nexthop
,
1265 const struct route_entry
*re
)
1267 json_object
*json_labels
= NULL
;
1268 json_object
*json_backups
= NULL
;
1269 json_object
*json_seg6local
= NULL
;
1270 json_object
*json_seg6
= NULL
;
1273 json_object_int_add(json_nexthop
, "flags", nexthop
->flags
);
1275 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
))
1276 json_object_boolean_true_add(json_nexthop
, "duplicate");
1278 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_FIB
))
1279 json_object_boolean_true_add(json_nexthop
, "fib");
1281 switch (nexthop
->type
) {
1282 case NEXTHOP_TYPE_IPV4
:
1283 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1284 json_object_string_addf(json_nexthop
, "ip", "%pI4",
1285 &nexthop
->gate
.ipv4
);
1286 json_object_string_add(json_nexthop
, "afi", "ipv4");
1288 if (nexthop
->ifindex
) {
1289 json_object_int_add(json_nexthop
, "interfaceIndex",
1291 json_object_string_add(json_nexthop
, "interfaceName",
1292 ifindex2ifname(nexthop
->ifindex
,
1296 case NEXTHOP_TYPE_IPV6
:
1297 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1298 json_object_string_addf(json_nexthop
, "ip", "%pI6",
1299 &nexthop
->gate
.ipv6
);
1300 json_object_string_add(json_nexthop
, "afi", "ipv6");
1302 if (nexthop
->ifindex
) {
1303 json_object_int_add(json_nexthop
, "interfaceIndex",
1305 json_object_string_add(json_nexthop
, "interfaceName",
1306 ifindex2ifname(nexthop
->ifindex
,
1311 case NEXTHOP_TYPE_IFINDEX
:
1312 json_object_boolean_true_add(json_nexthop
, "directlyConnected");
1313 json_object_int_add(json_nexthop
, "interfaceIndex",
1315 json_object_string_add(
1316 json_nexthop
, "interfaceName",
1317 ifindex2ifname(nexthop
->ifindex
, nexthop
->vrf_id
));
1319 case NEXTHOP_TYPE_BLACKHOLE
:
1320 json_object_boolean_true_add(json_nexthop
, "unreachable");
1321 switch (nexthop
->bh_type
) {
1322 case BLACKHOLE_REJECT
:
1323 json_object_boolean_true_add(json_nexthop
, "reject");
1325 case BLACKHOLE_ADMINPROHIB
:
1326 json_object_boolean_true_add(json_nexthop
,
1329 case BLACKHOLE_NULL
:
1330 json_object_boolean_true_add(json_nexthop
, "blackhole");
1332 case BLACKHOLE_UNSPEC
:
1338 /* This nexthop is a resolver for the parent nexthop.
1339 * Set resolver flag for better clarity and delimiter
1340 * in flat list of nexthops in json.
1342 if (nexthop
->rparent
)
1343 json_object_boolean_true_add(json_nexthop
, "resolver");
1345 if ((re
== NULL
|| (nexthop
->vrf_id
!= re
->vrf_id
)))
1346 json_object_string_add(json_nexthop
, "vrf",
1347 vrf_id_to_name(nexthop
->vrf_id
));
1349 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
))
1350 json_object_boolean_true_add(json_nexthop
, "duplicate");
1352 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
))
1353 json_object_boolean_true_add(json_nexthop
, "active");
1355 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ONLINK
))
1356 json_object_boolean_true_add(json_nexthop
, "onLink");
1358 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_LINKDOWN
))
1359 json_object_boolean_true_add(json_nexthop
, "linkDown");
1361 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_RECURSIVE
))
1362 json_object_boolean_true_add(json_nexthop
, "recursive");
1364 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1365 json_backups
= json_object_new_array();
1366 for (i
= 0; i
< nexthop
->backup_num
; i
++) {
1367 json_object_array_add(
1369 json_object_new_int(nexthop
->backup_idx
[i
]));
1372 json_object_object_add(json_nexthop
, "backupIndex",
1376 switch (nexthop
->type
) {
1377 case NEXTHOP_TYPE_IPV4
:
1378 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1379 if (nexthop
->src
.ipv4
.s_addr
)
1380 json_object_string_addf(json_nexthop
, "source", "%pI4",
1381 &nexthop
->src
.ipv4
);
1383 case NEXTHOP_TYPE_IPV6
:
1384 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1385 if (!IPV6_ADDR_SAME(&nexthop
->src
.ipv6
, &in6addr_any
))
1386 json_object_string_addf(json_nexthop
, "source", "%pI6",
1387 &nexthop
->src
.ipv6
);
1389 case NEXTHOP_TYPE_IFINDEX
:
1390 case NEXTHOP_TYPE_BLACKHOLE
:
1394 if (nexthop
->nh_label
&& nexthop
->nh_label
->num_labels
) {
1395 json_labels
= json_object_new_array();
1397 for (int label_index
= 0;
1398 label_index
< nexthop
->nh_label
->num_labels
; label_index
++)
1399 json_object_array_add(
1401 json_object_new_int((
1402 (nexthop
->nh_label_type
==
1405 &nexthop
->nh_label
->label
1407 : nexthop
->nh_label
->label
1410 json_object_object_add(json_nexthop
, "labels", json_labels
);
1413 if (nexthop
->weight
)
1414 json_object_int_add(json_nexthop
, "weight", nexthop
->weight
);
1416 if (nexthop
->srte_color
)
1417 json_object_int_add(json_nexthop
, "srteColor",
1418 nexthop
->srte_color
);
1420 if (nexthop
->nh_srv6
) {
1421 json_seg6local
= json_object_new_object();
1422 json_object_string_add(
1423 json_seg6local
, "action",
1424 seg6local_action2str(
1425 nexthop
->nh_srv6
->seg6local_action
));
1426 json_object_object_add(json_nexthop
, "seg6local",
1429 json_seg6
= json_object_new_object();
1430 json_object_string_addf(json_seg6
, "segs", "%pI6",
1431 &nexthop
->nh_srv6
->seg6_segs
);
1432 json_object_object_add(json_nexthop
, "seg6", json_seg6
);
1437 * Helper for nexthop output, used in the 'show ip route' path
1439 void show_route_nexthop_helper(struct vty
*vty
, const struct route_entry
*re
,
1440 const struct nexthop
*nexthop
)
1442 char buf
[MPLS_LABEL_STRLEN
];
1445 switch (nexthop
->type
) {
1446 case NEXTHOP_TYPE_IPV4
:
1447 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1448 vty_out(vty
, " via %pI4", &nexthop
->gate
.ipv4
);
1449 if (nexthop
->ifindex
)
1450 vty_out(vty
, ", %s",
1451 ifindex2ifname(nexthop
->ifindex
,
1454 case NEXTHOP_TYPE_IPV6
:
1455 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1456 vty_out(vty
, " via %s",
1457 inet_ntop(AF_INET6
, &nexthop
->gate
.ipv6
, buf
,
1459 if (nexthop
->ifindex
)
1460 vty_out(vty
, ", %s",
1461 ifindex2ifname(nexthop
->ifindex
,
1465 case NEXTHOP_TYPE_IFINDEX
:
1466 vty_out(vty
, " is directly connected, %s",
1467 ifindex2ifname(nexthop
->ifindex
, nexthop
->vrf_id
));
1469 case NEXTHOP_TYPE_BLACKHOLE
:
1470 vty_out(vty
, " unreachable");
1471 switch (nexthop
->bh_type
) {
1472 case BLACKHOLE_REJECT
:
1473 vty_out(vty
, " (ICMP unreachable)");
1475 case BLACKHOLE_ADMINPROHIB
:
1476 vty_out(vty
, " (ICMP admin-prohibited)");
1478 case BLACKHOLE_NULL
:
1479 vty_out(vty
, " (blackhole)");
1481 case BLACKHOLE_UNSPEC
:
1487 if ((re
== NULL
|| (nexthop
->vrf_id
!= re
->vrf_id
)))
1488 vty_out(vty
, " (vrf %s)", vrf_id_to_name(nexthop
->vrf_id
));
1490 if (!CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
))
1491 vty_out(vty
, " inactive");
1493 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ONLINK
))
1494 vty_out(vty
, " onlink");
1496 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_LINKDOWN
))
1497 vty_out(vty
, " linkdown");
1499 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_RECURSIVE
))
1500 vty_out(vty
, " (recursive)");
1502 switch (nexthop
->type
) {
1503 case NEXTHOP_TYPE_IPV4
:
1504 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1505 if (nexthop
->src
.ipv4
.s_addr
) {
1506 vty_out(vty
, ", src %pI4", &nexthop
->src
.ipv4
);
1507 /* SR-TE information */
1508 if (nexthop
->srte_color
)
1509 vty_out(vty
, ", SR-TE color %u",
1510 nexthop
->srte_color
);
1513 case NEXTHOP_TYPE_IPV6
:
1514 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1515 if (!IPV6_ADDR_SAME(&nexthop
->src
.ipv6
, &in6addr_any
))
1516 vty_out(vty
, ", src %pI6", &nexthop
->src
.ipv6
);
1518 case NEXTHOP_TYPE_IFINDEX
:
1519 case NEXTHOP_TYPE_BLACKHOLE
:
1523 /* Label information */
1524 if (nexthop
->nh_label
&& nexthop
->nh_label
->num_labels
) {
1525 vty_out(vty
, ", label %s",
1526 mpls_label2str(nexthop
->nh_label
->num_labels
,
1527 nexthop
->nh_label
->label
, buf
,
1528 sizeof(buf
), nexthop
->nh_label_type
, 1));
1531 if (nexthop
->nh_srv6
) {
1532 seg6local_context2str(buf
, sizeof(buf
),
1533 &nexthop
->nh_srv6
->seg6local_ctx
,
1534 nexthop
->nh_srv6
->seg6local_action
);
1535 vty_out(vty
, ", seg6local %s %s",
1536 seg6local_action2str(
1537 nexthop
->nh_srv6
->seg6local_action
),
1539 vty_out(vty
, ", seg6 %pI6", &nexthop
->nh_srv6
->seg6_segs
);
1542 if (nexthop
->weight
)
1543 vty_out(vty
, ", weight %u", nexthop
->weight
);
1545 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1546 vty_out(vty
, ", backup %d", nexthop
->backup_idx
[0]);
1548 for (i
= 1; i
< nexthop
->backup_num
; i
++)
1549 vty_out(vty
, ",%d", nexthop
->backup_idx
[i
]);
1553 static void print_rnh(struct route_node
*rn
, struct vty
*vty
, json_object
*json
)
1556 struct nexthop
*nexthop
;
1557 struct listnode
*node
;
1558 struct zserv
*client
;
1560 json_object
*json_nht
= NULL
;
1561 json_object
*json_client_array
= NULL
;
1562 json_object
*json_client
= NULL
;
1563 json_object
*json_nexthop_array
= NULL
;
1564 json_object
*json_nexthop
= NULL
;
1569 json_nht
= json_object_new_object();
1570 json_nexthop_array
= json_object_new_array();
1571 json_client_array
= json_object_new_array();
1573 json_object_object_add(
1575 inet_ntop(rn
->p
.family
, &rn
->p
.u
.prefix
, buf
, BUFSIZ
),
1577 json_object_boolean_add(
1578 json_nht
, "nhtConnected",
1579 CHECK_FLAG(rnh
->flags
, ZEBRA_NHT_CONNECTED
));
1580 json_object_object_add(json_nht
, "clientList",
1582 json_object_object_add(json_nht
, "nexthops",
1583 json_nexthop_array
);
1585 vty_out(vty
, "%s%s\n",
1586 inet_ntop(rn
->p
.family
, &rn
->p
.u
.prefix
, buf
, BUFSIZ
),
1587 CHECK_FLAG(rnh
->flags
, ZEBRA_NHT_CONNECTED
)
1594 json_object_string_add(
1595 json_nht
, "resolvedProtocol",
1596 zebra_route_string(rnh
->state
->type
));
1598 vty_out(vty
, " resolved via %s\n",
1599 zebra_route_string(rnh
->state
->type
));
1601 for (nexthop
= rnh
->state
->nhe
->nhg
.nexthop
; nexthop
;
1602 nexthop
= nexthop
->next
) {
1604 json_nexthop
= json_object_new_object();
1605 json_object_array_add(json_nexthop_array
,
1607 show_nexthop_json_helper(json_nexthop
, nexthop
,
1610 show_route_nexthop_helper(vty
, NULL
, nexthop
);
1616 json_object_boolean_add(
1617 json_nht
, "unresolved",
1618 CHECK_FLAG(rnh
->flags
, ZEBRA_NHT_CONNECTED
));
1620 vty_out(vty
, " unresolved%s\n",
1621 CHECK_FLAG(rnh
->flags
, ZEBRA_NHT_CONNECTED
)
1627 vty_out(vty
, " Client list:");
1629 for (ALL_LIST_ELEMENTS_RO(rnh
->client_list
, node
, client
)) {
1631 json_client
= json_object_new_object();
1632 json_object_array_add(json_client_array
, json_client
);
1634 json_object_string_add(
1635 json_client
, "protocol",
1636 zebra_route_string(client
->proto
));
1637 json_object_int_add(json_client
, "socket",
1639 json_object_string_add(json_client
, "protocolFiltered",
1640 (rnh
->filtered
[client
->proto
]
1644 vty_out(vty
, " %s(fd %d)%s",
1645 zebra_route_string(client
->proto
), client
->sock
,
1646 rnh
->filtered
[client
->proto
] ? "(filtered)"
1651 if (!list_isempty(rnh
->zebra_pseudowire_list
)) {
1653 json_object_boolean_true_add(json_nht
,
1654 "zebraPseudowires");
1656 vty_out(vty
, " zebra[pseudowires]");
1663 static int zebra_cleanup_rnh_client(vrf_id_t vrf_id
, afi_t afi
, safi_t safi
,
1664 struct zserv
*client
)
1666 struct route_table
*ntable
;
1667 struct route_node
*nrn
;
1670 if (IS_ZEBRA_DEBUG_NHT
) {
1671 struct vrf
*vrf
= vrf_lookup_by_id(vrf_id
);
1673 zlog_debug("%s(%u): Client %s RNH cleanup for family %s",
1674 VRF_LOGNAME(vrf
), vrf_id
,
1675 zebra_route_string(client
->proto
), afi2str(afi
));
1678 ntable
= get_rnh_table(vrf_id
, afi
, safi
);
1680 zlog_debug("cleanup_rnh_client: rnh table not found");
1684 for (nrn
= route_top(ntable
); nrn
; nrn
= route_next(nrn
)) {
1689 zebra_remove_rnh_client(rnh
, client
);
1694 /* Cleanup registered nexthops (across VRFs) upon client disconnect. */
1695 static int zebra_client_cleanup_rnh(struct zserv
*client
)
1698 struct zebra_vrf
*zvrf
;
1700 RB_FOREACH (vrf
, vrf_id_head
, &vrfs_by_id
) {
1703 zebra_cleanup_rnh_client(zvrf_id(zvrf
), AFI_IP
,
1704 SAFI_UNICAST
, client
);
1705 zebra_cleanup_rnh_client(zvrf_id(zvrf
), AFI_IP
,
1706 SAFI_MULTICAST
, client
);
1707 zebra_cleanup_rnh_client(zvrf_id(zvrf
), AFI_IP6
,
1708 SAFI_UNICAST
, client
);
1709 zebra_cleanup_rnh_client(zvrf_id(zvrf
), AFI_IP6
,
1710 SAFI_MULTICAST
, client
);
1717 int rnh_resolve_via_default(struct zebra_vrf
*zvrf
, int family
)
1719 if (((family
== AF_INET
) && zvrf
->zebra_rnh_ip_default_route
)
1720 || ((family
== AF_INET6
) && zvrf
->zebra_rnh_ipv6_default_route
))
1727 * UI control to avoid notifications if backup nexthop status changes
1729 void rnh_set_hide_backups(bool hide_p
)
1731 rnh_hide_backups
= hide_p
;
1734 bool rnh_get_hide_backups(void)
1736 return rnh_hide_backups
;