1 /* Zebra Nexthop Group Code.
2 * Copyright (C) 2019 Cumulus Networks, Inc.
6 * This file is part of FRR.
8 * FRR is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
13 * FRR is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with FRR; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 #include "lib/nexthop.h"
26 #include "lib/nexthop_group_private.h"
27 #include "lib/routemap.h"
29 #include "lib/jhash.h"
30 #include "lib/debug.h"
31 #include "lib/lib_errors.h"
33 #include "zebra/connected.h"
34 #include "zebra/debug.h"
35 #include "zebra/zebra_router.h"
36 #include "zebra/zebra_nhg_private.h"
37 #include "zebra/zebra_rnh.h"
38 #include "zebra/zebra_routemap.h"
39 #include "zebra/zebra_memory.h"
40 #include "zebra/zebra_srte.h"
41 #include "zebra/zserv.h"
43 #include "zebra_errors.h"
44 #include "zebra_dplane.h"
45 #include "zebra/interface.h"
47 DEFINE_MTYPE_STATIC(ZEBRA
, NHG
, "Nexthop Group Entry");
48 DEFINE_MTYPE_STATIC(ZEBRA
, NHG_CONNECTED
, "Nexthop Group Connected");
49 DEFINE_MTYPE_STATIC(ZEBRA
, NHG_CTX
, "Nexthop Group Context");
51 /* id counter to keep in sync with kernel */
55 static bool g_nexthops_enabled
= true;
56 static bool proto_nexthops_only
;
58 static struct nhg_hash_entry
*depends_find(const struct nexthop
*nh
, afi_t afi
,
60 static void depends_add(struct nhg_connected_tree_head
*head
,
61 struct nhg_hash_entry
*depend
);
62 static struct nhg_hash_entry
*
63 depends_find_add(struct nhg_connected_tree_head
*head
, struct nexthop
*nh
,
65 static struct nhg_hash_entry
*
66 depends_find_id_add(struct nhg_connected_tree_head
*head
, uint32_t id
);
67 static void depends_decrement_free(struct nhg_connected_tree_head
*head
);
69 static struct nhg_backup_info
*
70 nhg_backup_copy(const struct nhg_backup_info
*orig
);
72 /* Helper function for getting the next allocatable ID */
73 static uint32_t nhg_get_next_id(void)
78 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
79 zlog_debug("%s: ID %u checking", __func__
, id_counter
);
81 if (id_counter
== ZEBRA_NHG_PROTO_LOWER
) {
82 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
83 zlog_debug("%s: ID counter wrapped", __func__
);
89 if (zebra_nhg_lookup_id(id_counter
)) {
90 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
91 zlog_debug("%s: ID already exists", __func__
);
102 static void nhg_connected_free(struct nhg_connected
*dep
)
104 XFREE(MTYPE_NHG_CONNECTED
, dep
);
107 static struct nhg_connected
*nhg_connected_new(struct nhg_hash_entry
*nhe
)
109 struct nhg_connected
*new = NULL
;
111 new = XCALLOC(MTYPE_NHG_CONNECTED
, sizeof(struct nhg_connected
));
117 void nhg_connected_tree_free(struct nhg_connected_tree_head
*head
)
119 struct nhg_connected
*rb_node_dep
= NULL
;
121 if (!nhg_connected_tree_is_empty(head
)) {
122 frr_each_safe(nhg_connected_tree
, head
, rb_node_dep
) {
123 nhg_connected_tree_del(head
, rb_node_dep
);
124 nhg_connected_free(rb_node_dep
);
129 bool nhg_connected_tree_is_empty(const struct nhg_connected_tree_head
*head
)
131 return nhg_connected_tree_count(head
) ? false : true;
134 struct nhg_connected
*
135 nhg_connected_tree_root(struct nhg_connected_tree_head
*head
)
137 return nhg_connected_tree_first(head
);
140 struct nhg_hash_entry
*
141 nhg_connected_tree_del_nhe(struct nhg_connected_tree_head
*head
,
142 struct nhg_hash_entry
*depend
)
144 struct nhg_connected lookup
= {};
145 struct nhg_connected
*remove
= NULL
;
146 struct nhg_hash_entry
*removed_nhe
;
150 /* Lookup to find the element, then remove it */
151 remove
= nhg_connected_tree_find(head
, &lookup
);
153 /* Re-returning here just in case this API changes..
154 * the _del list api's are a bit undefined at the moment.
156 * So hopefully returning here will make it fail if the api
157 * changes to something different than currently expected.
159 remove
= nhg_connected_tree_del(head
, remove
);
161 /* If the entry was sucessfully removed, free the 'connected` struct */
163 removed_nhe
= remove
->nhe
;
164 nhg_connected_free(remove
);
171 /* Assuming UNIQUE RB tree. If this changes, assumptions here about
172 * insertion need to change.
174 struct nhg_hash_entry
*
175 nhg_connected_tree_add_nhe(struct nhg_connected_tree_head
*head
,
176 struct nhg_hash_entry
*depend
)
178 struct nhg_connected
*new = NULL
;
180 new = nhg_connected_new(depend
);
182 /* On success, NULL will be returned from the
185 if (new && (nhg_connected_tree_add(head
, new) == NULL
))
188 /* If it wasn't successful, it must be a duplicate. We enforce the
189 * unique property for the `nhg_connected` tree.
191 nhg_connected_free(new);
197 nhg_connected_tree_decrement_ref(struct nhg_connected_tree_head
*head
)
199 struct nhg_connected
*rb_node_dep
= NULL
;
201 frr_each_safe(nhg_connected_tree
, head
, rb_node_dep
) {
202 zebra_nhg_decrement_ref(rb_node_dep
->nhe
);
207 nhg_connected_tree_increment_ref(struct nhg_connected_tree_head
*head
)
209 struct nhg_connected
*rb_node_dep
= NULL
;
211 frr_each(nhg_connected_tree
, head
, rb_node_dep
) {
212 zebra_nhg_increment_ref(rb_node_dep
->nhe
);
216 struct nhg_hash_entry
*zebra_nhg_resolve(struct nhg_hash_entry
*nhe
)
218 if (CHECK_FLAG(nhe
->flags
, NEXTHOP_GROUP_RECURSIVE
)
219 && !zebra_nhg_depends_is_empty(nhe
)) {
220 nhe
= nhg_connected_tree_root(&nhe
->nhg_depends
)->nhe
;
221 return zebra_nhg_resolve(nhe
);
227 unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry
*nhe
)
229 return nhg_connected_tree_count(&nhe
->nhg_depends
);
232 bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry
*nhe
)
234 return nhg_connected_tree_is_empty(&nhe
->nhg_depends
);
237 static void zebra_nhg_depends_del(struct nhg_hash_entry
*from
,
238 struct nhg_hash_entry
*depend
)
240 nhg_connected_tree_del_nhe(&from
->nhg_depends
, depend
);
243 static void zebra_nhg_depends_init(struct nhg_hash_entry
*nhe
)
245 nhg_connected_tree_init(&nhe
->nhg_depends
);
248 unsigned int zebra_nhg_dependents_count(const struct nhg_hash_entry
*nhe
)
250 return nhg_connected_tree_count(&nhe
->nhg_dependents
);
254 bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry
*nhe
)
256 return nhg_connected_tree_is_empty(&nhe
->nhg_dependents
);
259 static void zebra_nhg_dependents_del(struct nhg_hash_entry
*from
,
260 struct nhg_hash_entry
*dependent
)
262 nhg_connected_tree_del_nhe(&from
->nhg_dependents
, dependent
);
265 static void zebra_nhg_dependents_add(struct nhg_hash_entry
*to
,
266 struct nhg_hash_entry
*dependent
)
268 nhg_connected_tree_add_nhe(&to
->nhg_dependents
, dependent
);
271 static void zebra_nhg_dependents_init(struct nhg_hash_entry
*nhe
)
273 nhg_connected_tree_init(&nhe
->nhg_dependents
);
276 /* Release this nhe from anything depending on it */
277 static void zebra_nhg_dependents_release(struct nhg_hash_entry
*nhe
)
279 struct nhg_connected
*rb_node_dep
= NULL
;
281 frr_each_safe(nhg_connected_tree
, &nhe
->nhg_dependents
, rb_node_dep
) {
282 zebra_nhg_depends_del(rb_node_dep
->nhe
, nhe
);
283 /* recheck validity of the dependent */
284 zebra_nhg_check_valid(rb_node_dep
->nhe
);
288 /* Release this nhe from anything that it depends on */
289 static void zebra_nhg_depends_release(struct nhg_hash_entry
*nhe
)
291 if (!zebra_nhg_depends_is_empty(nhe
)) {
292 struct nhg_connected
*rb_node_dep
= NULL
;
294 frr_each_safe(nhg_connected_tree
, &nhe
->nhg_depends
,
296 zebra_nhg_dependents_del(rb_node_dep
->nhe
, nhe
);
302 struct nhg_hash_entry
*zebra_nhg_lookup_id(uint32_t id
)
304 struct nhg_hash_entry lookup
= {};
307 return hash_lookup(zrouter
.nhgs_id
, &lookup
);
310 static int zebra_nhg_insert_id(struct nhg_hash_entry
*nhe
)
312 if (hash_lookup(zrouter
.nhgs_id
, nhe
)) {
314 EC_ZEBRA_NHG_TABLE_INSERT_FAILED
,
315 "Failed inserting NHG id=%u into the ID hash table, entry already exists",
320 hash_get(zrouter
.nhgs_id
, nhe
, hash_alloc_intern
);
325 static void zebra_nhg_set_if(struct nhg_hash_entry
*nhe
, struct interface
*ifp
)
328 if_nhg_dependents_add(ifp
, nhe
);
332 zebra_nhg_connect_depends(struct nhg_hash_entry
*nhe
,
333 struct nhg_connected_tree_head
*nhg_depends
)
335 struct nhg_connected
*rb_node_dep
= NULL
;
337 /* This has been allocated higher above in the stack. Could probably
338 * re-allocate and free the old stuff but just using the same memory
339 * for now. Otherwise, their might be a time trade-off for repeated
340 * alloc/frees as startup.
342 nhe
->nhg_depends
= *nhg_depends
;
344 /* Attach backpointer to anything that it depends on */
345 zebra_nhg_dependents_init(nhe
);
346 if (!zebra_nhg_depends_is_empty(nhe
)) {
347 frr_each(nhg_connected_tree
, &nhe
->nhg_depends
, rb_node_dep
) {
348 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
349 zlog_debug("%s: nhe %p (%u), dep %p (%u)",
350 __func__
, nhe
, nhe
->id
,
352 rb_node_dep
->nhe
->id
);
354 zebra_nhg_dependents_add(rb_node_dep
->nhe
, nhe
);
359 /* Init an nhe, for use in a hash lookup for example */
360 void zebra_nhe_init(struct nhg_hash_entry
*nhe
, afi_t afi
,
361 const struct nexthop
*nh
)
363 memset(nhe
, 0, sizeof(struct nhg_hash_entry
));
364 nhe
->vrf_id
= VRF_DEFAULT
;
365 nhe
->type
= ZEBRA_ROUTE_NHG
;
366 nhe
->afi
= AFI_UNSPEC
;
368 /* There are some special rules that apply to groups representing
371 if (nh
&& (nh
->next
== NULL
)) {
373 case (NEXTHOP_TYPE_IFINDEX
):
374 case (NEXTHOP_TYPE_BLACKHOLE
):
376 * This switch case handles setting the afi different
377 * for ipv4/v6 routes. Ifindex/blackhole nexthop
378 * objects cannot be ambiguous, they must be Address
379 * Family specific. If we get here, we will either use
380 * the AF of the route, or the one we got passed from
381 * here from the kernel.
385 case (NEXTHOP_TYPE_IPV4_IFINDEX
):
386 case (NEXTHOP_TYPE_IPV4
):
389 case (NEXTHOP_TYPE_IPV6_IFINDEX
):
390 case (NEXTHOP_TYPE_IPV6
):
397 struct nhg_hash_entry
*zebra_nhg_alloc(void)
399 struct nhg_hash_entry
*nhe
;
401 nhe
= XCALLOC(MTYPE_NHG
, sizeof(struct nhg_hash_entry
));
407 * Allocate new nhe and make shallow copy of 'orig'; no
408 * recursive info is copied.
410 struct nhg_hash_entry
*zebra_nhe_copy(const struct nhg_hash_entry
*orig
,
413 struct nhg_hash_entry
*nhe
;
415 nhe
= zebra_nhg_alloc();
419 nexthop_group_copy(&(nhe
->nhg
), &(orig
->nhg
));
421 nhe
->vrf_id
= orig
->vrf_id
;
422 nhe
->afi
= orig
->afi
;
423 nhe
->type
= orig
->type
? orig
->type
: ZEBRA_ROUTE_NHG
;
425 nhe
->dplane_ref
= zebra_router_get_next_sequence();
427 /* Copy backup info also, if present */
428 if (orig
->backup_info
)
429 nhe
->backup_info
= nhg_backup_copy(orig
->backup_info
);
434 /* Allocation via hash handler */
435 static void *zebra_nhg_hash_alloc(void *arg
)
437 struct nhg_hash_entry
*nhe
= NULL
;
438 struct nhg_hash_entry
*copy
= arg
;
440 nhe
= zebra_nhe_copy(copy
, copy
->id
);
442 /* Mark duplicate nexthops in a group at creation time. */
443 nexthop_group_mark_duplicates(&(nhe
->nhg
));
445 zebra_nhg_connect_depends(nhe
, &(copy
->nhg_depends
));
447 /* Add the ifp now if it's not a group or recursive and has ifindex */
448 if (zebra_nhg_depends_is_empty(nhe
) && nhe
->nhg
.nexthop
449 && nhe
->nhg
.nexthop
->ifindex
) {
450 struct interface
*ifp
= NULL
;
452 ifp
= if_lookup_by_index(nhe
->nhg
.nexthop
->ifindex
,
453 nhe
->nhg
.nexthop
->vrf_id
);
455 zebra_nhg_set_if(nhe
, ifp
);
458 EC_ZEBRA_IF_LOOKUP_FAILED
,
459 "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u",
460 nhe
->nhg
.nexthop
->ifindex
,
461 nhe
->nhg
.nexthop
->vrf_id
, nhe
->id
);
468 uint32_t zebra_nhg_hash_key(const void *arg
)
470 const struct nhg_hash_entry
*nhe
= arg
;
471 uint32_t key
= 0x5a351234;
472 uint32_t primary
= 0;
475 primary
= nexthop_group_hash(&(nhe
->nhg
));
476 if (nhe
->backup_info
)
477 backup
= nexthop_group_hash(&(nhe
->backup_info
->nhe
->nhg
));
479 key
= jhash_3words(primary
, backup
, nhe
->type
, key
);
481 key
= jhash_2words(nhe
->vrf_id
, nhe
->afi
, key
);
486 uint32_t zebra_nhg_id_key(const void *arg
)
488 const struct nhg_hash_entry
*nhe
= arg
;
493 /* Helper with common nhg/nhe nexthop comparison logic */
494 static bool nhg_compare_nexthops(const struct nexthop
*nh1
,
495 const struct nexthop
*nh2
)
497 assert(nh1
!= NULL
&& nh2
!= NULL
);
500 * We have to check the active flag of each individual one,
501 * not just the overall active_num. This solves the special case
502 * issue of a route with a nexthop group with one nexthop
503 * resolving to itself and thus marking it inactive. If we
504 * have two different routes each wanting to mark a different
505 * nexthop inactive, they need to hash to two different groups.
507 * If we just hashed on num_active, they would hash the same
508 * which is incorrect.
512 * -> 1.1.1.1 dummy1 (inactive)
517 * -> 1.1.2.1 dummy2 (inactive)
519 * Without checking each individual one, they would hash to
520 * the same group and both have 1.1.1.1 dummy1 marked inactive.
523 if (CHECK_FLAG(nh1
->flags
, NEXTHOP_FLAG_ACTIVE
)
524 != CHECK_FLAG(nh2
->flags
, NEXTHOP_FLAG_ACTIVE
))
527 if (!nexthop_same(nh1
, nh2
))
533 bool zebra_nhg_hash_equal(const void *arg1
, const void *arg2
)
535 const struct nhg_hash_entry
*nhe1
= arg1
;
536 const struct nhg_hash_entry
*nhe2
= arg2
;
537 struct nexthop
*nexthop1
;
538 struct nexthop
*nexthop2
;
540 /* No matter what if they equal IDs, assume equal */
541 if (nhe1
->id
&& nhe2
->id
&& (nhe1
->id
== nhe2
->id
))
544 if (nhe1
->type
!= nhe2
->type
)
547 if (nhe1
->vrf_id
!= nhe2
->vrf_id
)
550 if (nhe1
->afi
!= nhe2
->afi
)
553 /* Nexthops should be in-order, so we simply compare them in-place */
554 for (nexthop1
= nhe1
->nhg
.nexthop
, nexthop2
= nhe2
->nhg
.nexthop
;
555 nexthop1
&& nexthop2
;
556 nexthop1
= nexthop1
->next
, nexthop2
= nexthop2
->next
) {
558 if (!nhg_compare_nexthops(nexthop1
, nexthop2
))
562 /* Check for unequal list lengths */
563 if (nexthop1
|| nexthop2
)
566 /* If there's no backup info, comparison is done. */
567 if ((nhe1
->backup_info
== NULL
) && (nhe2
->backup_info
== NULL
))
570 /* Compare backup info also - test the easy things first */
571 if (nhe1
->backup_info
&& (nhe2
->backup_info
== NULL
))
573 if (nhe2
->backup_info
&& (nhe1
->backup_info
== NULL
))
576 /* Compare number of backups before actually comparing any */
577 for (nexthop1
= nhe1
->backup_info
->nhe
->nhg
.nexthop
,
578 nexthop2
= nhe2
->backup_info
->nhe
->nhg
.nexthop
;
579 nexthop1
&& nexthop2
;
580 nexthop1
= nexthop1
->next
, nexthop2
= nexthop2
->next
) {
584 /* Did we find the end of one list before the other? */
585 if (nexthop1
|| nexthop2
)
588 /* Have to compare the backup nexthops */
589 for (nexthop1
= nhe1
->backup_info
->nhe
->nhg
.nexthop
,
590 nexthop2
= nhe2
->backup_info
->nhe
->nhg
.nexthop
;
591 nexthop1
&& nexthop2
;
592 nexthop1
= nexthop1
->next
, nexthop2
= nexthop2
->next
) {
594 if (!nhg_compare_nexthops(nexthop1
, nexthop2
))
601 bool zebra_nhg_hash_id_equal(const void *arg1
, const void *arg2
)
603 const struct nhg_hash_entry
*nhe1
= arg1
;
604 const struct nhg_hash_entry
*nhe2
= arg2
;
606 return nhe1
->id
== nhe2
->id
;
609 static int zebra_nhg_process_grp(struct nexthop_group
*nhg
,
610 struct nhg_connected_tree_head
*depends
,
611 struct nh_grp
*grp
, uint8_t count
)
613 nhg_connected_tree_init(depends
);
615 for (int i
= 0; i
< count
; i
++) {
616 struct nhg_hash_entry
*depend
= NULL
;
617 /* We do not care about nexthop_grp.weight at
618 * this time. But we should figure out
619 * how to adapt this to our code in
622 depend
= depends_find_id_add(depends
, grp
[i
].id
);
627 "Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table",
633 * If this is a nexthop with its own group
634 * dependencies, add them as well. Not sure its
635 * even possible to have a group within a group
639 copy_nexthops(&nhg
->nexthop
, depend
->nhg
.nexthop
, NULL
);
645 static void handle_recursive_depend(struct nhg_connected_tree_head
*nhg_depends
,
646 struct nexthop
*nh
, afi_t afi
, int type
)
648 struct nhg_hash_entry
*depend
= NULL
;
649 struct nexthop_group resolved_ng
= {};
651 resolved_ng
.nexthop
= nh
;
653 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
654 zlog_debug("%s: head %p, nh %pNHv",
655 __func__
, nhg_depends
, nh
);
657 depend
= zebra_nhg_rib_find(0, &resolved_ng
, afi
, type
);
659 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
660 zlog_debug("%s: nh %pNHv => %p (%u)",
661 __func__
, nh
, depend
,
662 depend
? depend
->id
: 0);
665 depends_add(nhg_depends
, depend
);
669 * Lookup an nhe in the global hash, using data from another nhe. If 'lookup'
670 * has an id value, that's used. Create a new global/shared nhe if not found.
672 static bool zebra_nhe_find(struct nhg_hash_entry
**nhe
, /* return value */
673 struct nhg_hash_entry
*lookup
,
674 struct nhg_connected_tree_head
*nhg_depends
,
677 bool created
= false;
678 bool recursive
= false;
679 struct nhg_hash_entry
*newnhe
, *backup_nhe
;
680 struct nexthop
*nh
= NULL
;
682 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
683 zlog_debug("%s: id %u, lookup %p, vrf %d, type %d, depends %p",
684 __func__
, lookup
->id
, lookup
,
685 lookup
->vrf_id
, lookup
->type
,
689 (*nhe
) = zebra_nhg_lookup_id(lookup
->id
);
691 (*nhe
) = hash_lookup(zrouter
.nhgs
, lookup
);
693 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
694 zlog_debug("%s: lookup => %p (%u)",
696 (*nhe
) ? (*nhe
)->id
: 0);
698 /* If we found an existing object, we're done */
702 /* We're going to create/insert a new nhe:
703 * assign the next global id value if necessary.
706 lookup
->id
= nhg_get_next_id();
708 if (lookup
->id
< ZEBRA_NHG_PROTO_LOWER
) {
710 * This is a zebra hashed/owned NHG.
712 * It goes in HASH and ID table.
714 newnhe
= hash_get(zrouter
.nhgs
, lookup
, zebra_nhg_hash_alloc
);
715 zebra_nhg_insert_id(newnhe
);
718 * This is upperproto owned NHG and should not be hashed to.
720 * It goes in ID table.
723 hash_get(zrouter
.nhgs_id
, lookup
, zebra_nhg_hash_alloc
);
728 /* Mail back the new object */
731 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
732 zlog_debug("%s: => created %p (%u)", __func__
, newnhe
,
735 /* Only hash/lookup the depends if the first lookup
736 * fails to find something. This should hopefully save a
737 * lot of cycles for larger ecmp sizes.
740 /* If you don't want to hash on each nexthop in the
741 * nexthop group struct you can pass the depends
742 * directly. Kernel-side we do this since it just looks
745 zebra_nhg_connect_depends(newnhe
, nhg_depends
);
749 /* Prepare dependency relationships if this is not a
750 * singleton nexthop. There are two cases: a single
751 * recursive nexthop, where we need a relationship to the
752 * resolving nexthop; or a group of nexthops, where we need
753 * relationships with the corresponding singletons.
755 zebra_nhg_depends_init(lookup
);
757 nh
= newnhe
->nhg
.nexthop
;
759 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_ACTIVE
))
760 SET_FLAG(newnhe
->flags
, NEXTHOP_GROUP_VALID
);
762 if (nh
->next
== NULL
&& newnhe
->id
< ZEBRA_NHG_PROTO_LOWER
) {
763 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_RECURSIVE
)) {
764 /* Single recursive nexthop */
765 handle_recursive_depend(&newnhe
->nhg_depends
,
771 /* Proto-owned are groups by default */
772 /* List of nexthops */
773 for (nh
= newnhe
->nhg
.nexthop
; nh
; nh
= nh
->next
) {
774 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
775 zlog_debug("%s: depends NH %pNHv %s",
777 CHECK_FLAG(nh
->flags
,
778 NEXTHOP_FLAG_RECURSIVE
) ?
781 depends_find_add(&newnhe
->nhg_depends
, nh
, afi
,
787 SET_FLAG((*nhe
)->flags
, NEXTHOP_GROUP_RECURSIVE
);
789 if (zebra_nhg_get_backup_nhg(newnhe
) == NULL
||
790 zebra_nhg_get_backup_nhg(newnhe
)->nexthop
== NULL
)
793 /* If there are backup nexthops, add them to the backup
794 * depends tree. The rules here are a little different.
797 backup_nhe
= newnhe
->backup_info
->nhe
;
799 nh
= backup_nhe
->nhg
.nexthop
;
801 /* Singleton recursive NH */
802 if (nh
->next
== NULL
&&
803 CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_RECURSIVE
)) {
804 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
805 zlog_debug("%s: backup depend NH %pNHv (R)",
808 /* Single recursive nexthop */
809 handle_recursive_depend(&backup_nhe
->nhg_depends
, nh
->resolved
,
810 afi
, backup_nhe
->type
);
813 /* One or more backup NHs */
814 for (; nh
; nh
= nh
->next
) {
815 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
816 zlog_debug("%s: backup depend NH %pNHv %s",
818 CHECK_FLAG(nh
->flags
,
819 NEXTHOP_FLAG_RECURSIVE
) ?
822 depends_find_add(&backup_nhe
->nhg_depends
, nh
, afi
,
828 SET_FLAG(backup_nhe
->flags
, NEXTHOP_GROUP_RECURSIVE
);
836 * Lookup or create an nhe, based on an nhg or an nhe id.
838 static bool zebra_nhg_find(struct nhg_hash_entry
**nhe
, uint32_t id
,
839 struct nexthop_group
*nhg
,
840 struct nhg_connected_tree_head
*nhg_depends
,
841 vrf_id_t vrf_id
, afi_t afi
, int type
)
843 struct nhg_hash_entry lookup
= {};
844 bool created
= false;
846 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
847 zlog_debug("%s: id %u, nhg %p, vrf %d, type %d, depends %p",
848 __func__
, id
, nhg
, vrf_id
, type
,
851 /* Use a temporary nhe and call into the superset/common code */
853 lookup
.type
= type
? type
: ZEBRA_ROUTE_NHG
;
856 lookup
.vrf_id
= vrf_id
;
857 if (lookup
.nhg
.nexthop
->next
) {
858 /* Groups can have all vrfs and AF's in them */
859 lookup
.afi
= AFI_UNSPEC
;
861 switch (lookup
.nhg
.nexthop
->type
) {
862 case (NEXTHOP_TYPE_IFINDEX
):
863 case (NEXTHOP_TYPE_BLACKHOLE
):
865 * This switch case handles setting the afi different
866 * for ipv4/v6 routes. Ifindex/blackhole nexthop
867 * objects cannot be ambiguous, they must be Address
868 * Family specific. If we get here, we will either use
869 * the AF of the route, or the one we got passed from
870 * here from the kernel.
874 case (NEXTHOP_TYPE_IPV4_IFINDEX
):
875 case (NEXTHOP_TYPE_IPV4
):
878 case (NEXTHOP_TYPE_IPV6_IFINDEX
):
879 case (NEXTHOP_TYPE_IPV6
):
880 lookup
.afi
= AFI_IP6
;
885 created
= zebra_nhe_find(nhe
, &lookup
, nhg_depends
, afi
);
890 /* Find/create a single nexthop */
891 static struct nhg_hash_entry
*
892 zebra_nhg_find_nexthop(uint32_t id
, struct nexthop
*nh
, afi_t afi
, int type
)
894 struct nhg_hash_entry
*nhe
= NULL
;
895 struct nexthop_group nhg
= {};
896 vrf_id_t vrf_id
= !vrf_is_backend_netns() ? VRF_DEFAULT
: nh
->vrf_id
;
898 nexthop_group_add_sorted(&nhg
, nh
);
900 zebra_nhg_find(&nhe
, id
, &nhg
, NULL
, vrf_id
, afi
, type
);
902 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
903 zlog_debug("%s: nh %pNHv => %p (%u)",
904 __func__
, nh
, nhe
, nhe
? nhe
->id
: 0);
909 static uint32_t nhg_ctx_get_id(const struct nhg_ctx
*ctx
)
914 static void nhg_ctx_set_status(struct nhg_ctx
*ctx
, enum nhg_ctx_status status
)
916 ctx
->status
= status
;
919 static enum nhg_ctx_status
nhg_ctx_get_status(const struct nhg_ctx
*ctx
)
924 static void nhg_ctx_set_op(struct nhg_ctx
*ctx
, enum nhg_ctx_op_e op
)
929 static enum nhg_ctx_op_e
nhg_ctx_get_op(const struct nhg_ctx
*ctx
)
934 static vrf_id_t
nhg_ctx_get_vrf_id(const struct nhg_ctx
*ctx
)
939 static int nhg_ctx_get_type(const struct nhg_ctx
*ctx
)
944 static int nhg_ctx_get_afi(const struct nhg_ctx
*ctx
)
949 static struct nexthop
*nhg_ctx_get_nh(struct nhg_ctx
*ctx
)
954 static uint8_t nhg_ctx_get_count(const struct nhg_ctx
*ctx
)
959 static struct nh_grp
*nhg_ctx_get_grp(struct nhg_ctx
*ctx
)
964 static struct nhg_ctx
*nhg_ctx_new(void)
968 new = XCALLOC(MTYPE_NHG_CTX
, sizeof(struct nhg_ctx
));
973 static void nhg_ctx_free(struct nhg_ctx
**ctx
)
980 assert((*ctx
) != NULL
);
982 if (nhg_ctx_get_count(*ctx
))
985 nh
= nhg_ctx_get_nh(*ctx
);
987 nexthop_del_labels(nh
);
990 XFREE(MTYPE_NHG_CTX
, *ctx
);
993 static struct nhg_ctx
*nhg_ctx_init(uint32_t id
, struct nexthop
*nh
,
994 struct nh_grp
*grp
, vrf_id_t vrf_id
,
995 afi_t afi
, int type
, uint8_t count
)
997 struct nhg_ctx
*ctx
= NULL
;
1002 ctx
->vrf_id
= vrf_id
;
1008 /* Copy over the array */
1009 memcpy(&ctx
->u
.grp
, grp
, count
* sizeof(struct nh_grp
));
1016 static void zebra_nhg_set_valid(struct nhg_hash_entry
*nhe
)
1018 struct nhg_connected
*rb_node_dep
;
1020 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_VALID
);
1022 frr_each(nhg_connected_tree
, &nhe
->nhg_dependents
, rb_node_dep
)
1023 zebra_nhg_set_valid(rb_node_dep
->nhe
);
1026 static void zebra_nhg_set_invalid(struct nhg_hash_entry
*nhe
)
1028 struct nhg_connected
*rb_node_dep
;
1030 UNSET_FLAG(nhe
->flags
, NEXTHOP_GROUP_VALID
);
1032 /* Update validity of nexthops depending on it */
1033 frr_each(nhg_connected_tree
, &nhe
->nhg_dependents
, rb_node_dep
)
1034 zebra_nhg_check_valid(rb_node_dep
->nhe
);
1037 void zebra_nhg_check_valid(struct nhg_hash_entry
*nhe
)
1039 struct nhg_connected
*rb_node_dep
= NULL
;
1042 /* If anthing else in the group is valid, the group is valid */
1043 frr_each(nhg_connected_tree
, &nhe
->nhg_depends
, rb_node_dep
) {
1044 if (CHECK_FLAG(rb_node_dep
->nhe
->flags
, NEXTHOP_GROUP_VALID
)) {
1052 zebra_nhg_set_valid(nhe
);
1054 zebra_nhg_set_invalid(nhe
);
1057 static void zebra_nhg_release_all_deps(struct nhg_hash_entry
*nhe
)
1059 /* Remove it from any lists it may be on */
1060 zebra_nhg_depends_release(nhe
);
1061 zebra_nhg_dependents_release(nhe
);
1063 if_nhg_dependents_del(nhe
->ifp
, nhe
);
1066 static void zebra_nhg_release(struct nhg_hash_entry
*nhe
)
1068 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1069 zlog_debug("%s: nhe %p (%u)", __func__
, nhe
, nhe
->id
);
1071 zebra_nhg_release_all_deps(nhe
);
1074 * If its not zebra owned, we didn't store it here and have to be
1075 * sure we don't clear one thats actually being used.
1077 if (nhe
->id
< ZEBRA_NHG_PROTO_LOWER
)
1078 hash_release(zrouter
.nhgs
, nhe
);
1080 hash_release(zrouter
.nhgs_id
, nhe
);
1083 static void zebra_nhg_handle_uninstall(struct nhg_hash_entry
*nhe
)
1085 zebra_nhg_release(nhe
);
1086 zebra_nhg_free(nhe
);
1089 static void zebra_nhg_handle_install(struct nhg_hash_entry
*nhe
)
1091 /* Update validity of groups depending on it */
1092 struct nhg_connected
*rb_node_dep
;
1094 frr_each_safe(nhg_connected_tree
, &nhe
->nhg_dependents
, rb_node_dep
)
1095 zebra_nhg_set_valid(rb_node_dep
->nhe
);
1099 * The kernel/other program has changed the state of a nexthop object we are
1102 static void zebra_nhg_handle_kernel_state_change(struct nhg_hash_entry
*nhe
,
1108 "Kernel %s a nexthop group with ID (%u) that we are still using for a route, sending it back down",
1109 (is_delete
? "deleted" : "updated"), nhe
->id
);
1111 UNSET_FLAG(nhe
->flags
, NEXTHOP_GROUP_INSTALLED
);
1112 zebra_nhg_install_kernel(nhe
);
1114 zebra_nhg_handle_uninstall(nhe
);
1117 static int nhg_ctx_process_new(struct nhg_ctx
*ctx
)
1119 struct nexthop_group
*nhg
= NULL
;
1120 struct nhg_connected_tree_head nhg_depends
= {};
1121 struct nhg_hash_entry
*lookup
= NULL
;
1122 struct nhg_hash_entry
*nhe
= NULL
;
1124 uint32_t id
= nhg_ctx_get_id(ctx
);
1125 uint8_t count
= nhg_ctx_get_count(ctx
);
1126 vrf_id_t vrf_id
= nhg_ctx_get_vrf_id(ctx
);
1127 int type
= nhg_ctx_get_type(ctx
);
1128 afi_t afi
= nhg_ctx_get_afi(ctx
);
1130 lookup
= zebra_nhg_lookup_id(id
);
1132 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1133 zlog_debug("%s: id %u, count %d, lookup => %p",
1134 __func__
, id
, count
, lookup
);
1137 /* This is already present in our table, hence an update
1138 * that we did not initate.
1140 zebra_nhg_handle_kernel_state_change(lookup
, false);
1144 if (nhg_ctx_get_count(ctx
)) {
1145 nhg
= nexthop_group_new();
1146 if (zebra_nhg_process_grp(nhg
, &nhg_depends
,
1147 nhg_ctx_get_grp(ctx
), count
)) {
1148 depends_decrement_free(&nhg_depends
);
1149 nexthop_group_delete(&nhg
);
1153 if (!zebra_nhg_find(&nhe
, id
, nhg
, &nhg_depends
, vrf_id
, afi
,
1155 depends_decrement_free(&nhg_depends
);
1157 /* These got copied over in zebra_nhg_alloc() */
1158 nexthop_group_delete(&nhg
);
1160 nhe
= zebra_nhg_find_nexthop(id
, nhg_ctx_get_nh(ctx
), afi
,
1165 EC_ZEBRA_TABLE_LOOKUP_FAILED
,
1166 "Zebra failed to find or create a nexthop hash entry for ID (%u)",
1171 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1172 zlog_debug("%s: nhe %p (%u) is new", __func__
, nhe
, nhe
->id
);
1174 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_VALID
);
1175 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_INSTALLED
);
1180 static int nhg_ctx_process_del(struct nhg_ctx
*ctx
)
1182 struct nhg_hash_entry
*nhe
= NULL
;
1183 uint32_t id
= nhg_ctx_get_id(ctx
);
1185 nhe
= zebra_nhg_lookup_id(id
);
1189 EC_ZEBRA_BAD_NHG_MESSAGE
,
1190 "Kernel delete message received for nexthop group ID (%u) that we do not have in our ID table",
1195 zebra_nhg_handle_kernel_state_change(nhe
, true);
1200 static void nhg_ctx_fini(struct nhg_ctx
**ctx
)
1203 * Just freeing for now, maybe do something more in the future
1210 static int queue_add(struct nhg_ctx
*ctx
)
1212 /* If its queued or already processed do nothing */
1213 if (nhg_ctx_get_status(ctx
) == NHG_CTX_QUEUED
)
1216 if (rib_queue_nhg_add(ctx
)) {
1217 nhg_ctx_set_status(ctx
, NHG_CTX_FAILURE
);
1221 nhg_ctx_set_status(ctx
, NHG_CTX_QUEUED
);
1226 int nhg_ctx_process(struct nhg_ctx
*ctx
)
1230 switch (nhg_ctx_get_op(ctx
)) {
1231 case NHG_CTX_OP_NEW
:
1232 ret
= nhg_ctx_process_new(ctx
);
1233 if (nhg_ctx_get_count(ctx
) && ret
== -ENOENT
1234 && nhg_ctx_get_status(ctx
) != NHG_CTX_REQUEUED
) {
1236 * We have entered a situation where we are
1237 * processing a group from the kernel
1238 * that has a contained nexthop which
1239 * we have not yet processed.
1241 * Re-enqueue this ctx to be handled exactly one
1242 * more time (indicated by the flag).
1244 * By the time we get back to it, we
1245 * should have processed its depends.
1247 nhg_ctx_set_status(ctx
, NHG_CTX_NONE
);
1248 if (queue_add(ctx
) == 0) {
1249 nhg_ctx_set_status(ctx
, NHG_CTX_REQUEUED
);
1254 case NHG_CTX_OP_DEL
:
1255 ret
= nhg_ctx_process_del(ctx
);
1256 case NHG_CTX_OP_NONE
:
1260 nhg_ctx_set_status(ctx
, (ret
? NHG_CTX_FAILURE
: NHG_CTX_SUCCESS
));
1267 /* Kernel-side, you either get a single new nexthop or a array of ID's */
1268 int zebra_nhg_kernel_find(uint32_t id
, struct nexthop
*nh
, struct nh_grp
*grp
,
1269 uint8_t count
, vrf_id_t vrf_id
, afi_t afi
, int type
,
1272 struct nhg_ctx
*ctx
= NULL
;
1274 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1275 zlog_debug("%s: nh %pNHv, id %u, count %d",
1276 __func__
, nh
, id
, (int)count
);
1278 if (id
> id_counter
&& id
< ZEBRA_NHG_PROTO_LOWER
)
1279 /* Increase our counter so we don't try to create
1280 * an ID that already exists
1284 ctx
= nhg_ctx_init(id
, nh
, grp
, vrf_id
, afi
, type
, count
);
1285 nhg_ctx_set_op(ctx
, NHG_CTX_OP_NEW
);
1287 /* Under statup conditions, we need to handle them immediately
1288 * like we do for routes. Otherwise, we are going to get a route
1289 * with a nhe_id that we have not handled.
1292 return nhg_ctx_process(ctx
);
1294 if (queue_add(ctx
)) {
1302 /* Kernel-side, received delete message */
1303 int zebra_nhg_kernel_del(uint32_t id
, vrf_id_t vrf_id
)
1305 struct nhg_ctx
*ctx
= NULL
;
1307 ctx
= nhg_ctx_init(id
, NULL
, NULL
, vrf_id
, 0, 0, 0);
1309 nhg_ctx_set_op(ctx
, NHG_CTX_OP_DEL
);
1311 if (queue_add(ctx
)) {
1319 /* Some dependency helper functions */
1320 static struct nhg_hash_entry
*depends_find_recursive(const struct nexthop
*nh
,
1321 afi_t afi
, int type
)
1323 struct nhg_hash_entry
*nhe
;
1324 struct nexthop
*lookup
= NULL
;
1326 lookup
= nexthop_dup(nh
, NULL
);
1328 nhe
= zebra_nhg_find_nexthop(0, lookup
, afi
, type
);
1330 nexthops_free(lookup
);
1335 static struct nhg_hash_entry
*depends_find_singleton(const struct nexthop
*nh
,
1336 afi_t afi
, int type
)
1338 struct nhg_hash_entry
*nhe
;
1339 struct nexthop lookup
= {};
1341 /* Capture a snapshot of this single nh; it might be part of a list,
1342 * so we need to make a standalone copy.
1344 nexthop_copy_no_recurse(&lookup
, nh
, NULL
);
1346 nhe
= zebra_nhg_find_nexthop(0, &lookup
, afi
, type
);
1348 /* The copy may have allocated labels; free them if necessary. */
1349 nexthop_del_labels(&lookup
);
1351 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1352 zlog_debug("%s: nh %pNHv => %p (%u)",
1353 __func__
, nh
, nhe
, nhe
? nhe
->id
: 0);
1358 static struct nhg_hash_entry
*depends_find(const struct nexthop
*nh
, afi_t afi
,
1361 struct nhg_hash_entry
*nhe
= NULL
;
1366 /* We are separating these functions out to increase handling speed
1367 * in the non-recursive case (by not alloc/freeing)
1369 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_RECURSIVE
))
1370 nhe
= depends_find_recursive(nh
, afi
, type
);
1372 nhe
= depends_find_singleton(nh
, afi
, type
);
1375 if (IS_ZEBRA_DEBUG_NHG_DETAIL
) {
1376 zlog_debug("%s: nh %pNHv %s => %p (%u)", __func__
, nh
,
1377 CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_RECURSIVE
) ? "(R)"
1379 nhe
, nhe
? nhe
->id
: 0);
1386 static void depends_add(struct nhg_connected_tree_head
*head
,
1387 struct nhg_hash_entry
*depend
)
1389 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1390 zlog_debug("%s: head %p nh %pNHv",
1391 __func__
, head
, depend
->nhg
.nexthop
);
1393 /* If NULL is returned, it was successfully added and
1394 * needs to have its refcnt incremented.
1396 * Else the NHE is already present in the tree and doesn't
1397 * need to increment the refcnt.
1399 if (nhg_connected_tree_add_nhe(head
, depend
) == NULL
)
1400 zebra_nhg_increment_ref(depend
);
1403 static struct nhg_hash_entry
*
1404 depends_find_add(struct nhg_connected_tree_head
*head
, struct nexthop
*nh
,
1405 afi_t afi
, int type
)
1407 struct nhg_hash_entry
*depend
= NULL
;
1409 depend
= depends_find(nh
, afi
, type
);
1411 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1412 zlog_debug("%s: nh %pNHv => %p",
1413 __func__
, nh
, depend
);
1416 depends_add(head
, depend
);
1421 static struct nhg_hash_entry
*
1422 depends_find_id_add(struct nhg_connected_tree_head
*head
, uint32_t id
)
1424 struct nhg_hash_entry
*depend
= NULL
;
1426 depend
= zebra_nhg_lookup_id(id
);
1429 depends_add(head
, depend
);
1434 static void depends_decrement_free(struct nhg_connected_tree_head
*head
)
1436 nhg_connected_tree_decrement_ref(head
);
1437 nhg_connected_tree_free(head
);
1440 /* Find an nhe based on a list of nexthops */
1441 struct nhg_hash_entry
*zebra_nhg_rib_find(uint32_t id
,
1442 struct nexthop_group
*nhg
,
1443 afi_t rt_afi
, int type
)
1445 struct nhg_hash_entry
*nhe
= NULL
;
1449 * CLANG SA is complaining that nexthop may be NULL
1450 * Make it happy but this is ridonc
1452 assert(nhg
->nexthop
);
1453 vrf_id
= !vrf_is_backend_netns() ? VRF_DEFAULT
: nhg
->nexthop
->vrf_id
;
1455 zebra_nhg_find(&nhe
, id
, nhg
, NULL
, vrf_id
, rt_afi
, type
);
1457 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1458 zlog_debug("%s: => nhe %p (%u)",
1459 __func__
, nhe
, nhe
? nhe
->id
: 0);
1464 /* Find an nhe based on a route's nhe */
1465 struct nhg_hash_entry
*
1466 zebra_nhg_rib_find_nhe(struct nhg_hash_entry
*rt_nhe
, afi_t rt_afi
)
1468 struct nhg_hash_entry
*nhe
= NULL
;
1470 if (!(rt_nhe
&& rt_nhe
->nhg
.nexthop
)) {
1471 flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED
,
1472 "No nexthop passed to %s", __func__
);
1476 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1477 zlog_debug("%s: rt_nhe %p (%u)", __func__
, rt_nhe
, rt_nhe
->id
);
1479 zebra_nhe_find(&nhe
, rt_nhe
, NULL
, rt_afi
);
1481 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1482 zlog_debug("%s: => nhe %p (%u)",
1483 __func__
, nhe
, nhe
? nhe
->id
: 0);
1489 * Allocate backup nexthop info object. Typically these are embedded in
1490 * nhg_hash_entry objects.
1492 struct nhg_backup_info
*zebra_nhg_backup_alloc(void)
1494 struct nhg_backup_info
*p
;
1496 p
= XCALLOC(MTYPE_NHG
, sizeof(struct nhg_backup_info
));
1498 p
->nhe
= zebra_nhg_alloc();
1500 /* Identify the embedded group used to hold the list of backups */
1501 SET_FLAG(p
->nhe
->flags
, NEXTHOP_GROUP_BACKUP
);
1507 * Free backup nexthop info object, deal with any embedded allocations
1509 void zebra_nhg_backup_free(struct nhg_backup_info
**p
)
1513 zebra_nhg_free((*p
)->nhe
);
1515 XFREE(MTYPE_NHG
, (*p
));
1519 /* Accessor for backup nexthop group */
1520 struct nexthop_group
*zebra_nhg_get_backup_nhg(struct nhg_hash_entry
*nhe
)
1522 struct nexthop_group
*p
= NULL
;
1525 if (nhe
->backup_info
&& nhe
->backup_info
->nhe
)
1526 p
= &(nhe
->backup_info
->nhe
->nhg
);
1533 * Helper to return a copy of a backup_info - note that this is a shallow
1534 * copy, meant to be used when creating a new nhe from info passed in with
1537 static struct nhg_backup_info
*
1538 nhg_backup_copy(const struct nhg_backup_info
*orig
)
1540 struct nhg_backup_info
*b
;
1542 b
= zebra_nhg_backup_alloc();
1544 /* Copy list of nexthops */
1545 nexthop_group_copy(&(b
->nhe
->nhg
), &(orig
->nhe
->nhg
));
1550 static void zebra_nhg_free_members(struct nhg_hash_entry
*nhe
)
1552 nexthops_free(nhe
->nhg
.nexthop
);
1554 zebra_nhg_backup_free(&nhe
->backup_info
);
1556 /* Decrement to remove connection ref */
1557 nhg_connected_tree_decrement_ref(&nhe
->nhg_depends
);
1558 nhg_connected_tree_free(&nhe
->nhg_depends
);
1559 nhg_connected_tree_free(&nhe
->nhg_dependents
);
1562 void zebra_nhg_free(struct nhg_hash_entry
*nhe
)
1564 if (IS_ZEBRA_DEBUG_NHG_DETAIL
) {
1565 /* Group or singleton? */
1566 if (nhe
->nhg
.nexthop
&& nhe
->nhg
.nexthop
->next
)
1567 zlog_debug("%s: nhe %p (%u), refcnt %d",
1568 __func__
, nhe
, nhe
->id
, nhe
->refcnt
);
1570 zlog_debug("%s: nhe %p (%u), refcnt %d, NH %pNHv",
1571 __func__
, nhe
, nhe
->id
, nhe
->refcnt
,
1576 zlog_debug("nhe_id=%u hash refcnt=%d", nhe
->id
, nhe
->refcnt
);
1578 zebra_nhg_free_members(nhe
);
1580 XFREE(MTYPE_NHG
, nhe
);
1583 void zebra_nhg_hash_free(void *p
)
1585 zebra_nhg_free((struct nhg_hash_entry
*)p
);
1588 void zebra_nhg_decrement_ref(struct nhg_hash_entry
*nhe
)
1590 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1591 zlog_debug("%s: nhe %p (%u) %d => %d",
1592 __func__
, nhe
, nhe
->id
, nhe
->refcnt
,
1597 if (!zebra_nhg_depends_is_empty(nhe
))
1598 nhg_connected_tree_decrement_ref(&nhe
->nhg_depends
);
1600 if (ZEBRA_NHG_CREATED(nhe
) && nhe
->refcnt
<= 0)
1601 zebra_nhg_uninstall_kernel(nhe
);
1604 void zebra_nhg_increment_ref(struct nhg_hash_entry
*nhe
)
1606 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1607 zlog_debug("%s: nhe %p (%u) %d => %d",
1608 __func__
, nhe
, nhe
->id
, nhe
->refcnt
,
1613 if (!zebra_nhg_depends_is_empty(nhe
))
1614 nhg_connected_tree_increment_ref(&nhe
->nhg_depends
);
1617 static void nexthop_set_resolved(afi_t afi
, const struct nexthop
*newhop
,
1618 struct nexthop
*nexthop
,
1619 struct zebra_sr_policy
*policy
)
1621 struct nexthop
*resolved_hop
;
1622 uint8_t num_labels
= 0;
1623 mpls_label_t labels
[MPLS_MAX_LABELS
];
1624 enum lsp_types_t label_type
= ZEBRA_LSP_NONE
;
1627 resolved_hop
= nexthop_new();
1628 SET_FLAG(resolved_hop
->flags
, NEXTHOP_FLAG_ACTIVE
);
1630 resolved_hop
->vrf_id
= nexthop
->vrf_id
;
1631 switch (newhop
->type
) {
1632 case NEXTHOP_TYPE_IPV4
:
1633 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1634 /* If the resolving route specifies a gateway, use it */
1635 resolved_hop
->type
= newhop
->type
;
1636 resolved_hop
->gate
.ipv4
= newhop
->gate
.ipv4
;
1638 if (newhop
->ifindex
) {
1639 resolved_hop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
1640 resolved_hop
->ifindex
= newhop
->ifindex
;
1643 case NEXTHOP_TYPE_IPV6
:
1644 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1645 resolved_hop
->type
= newhop
->type
;
1646 resolved_hop
->gate
.ipv6
= newhop
->gate
.ipv6
;
1648 if (newhop
->ifindex
) {
1649 resolved_hop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
1650 resolved_hop
->ifindex
= newhop
->ifindex
;
1653 case NEXTHOP_TYPE_IFINDEX
:
1654 /* If the resolving route is an interface route,
1655 * it means the gateway we are looking up is connected
1656 * to that interface. (The actual network is _not_ onlink).
1657 * Therefore, the resolved route should have the original
1658 * gateway as nexthop as it is directly connected.
1660 * On Linux, we have to set the onlink netlink flag because
1661 * otherwise, the kernel won't accept the route.
1663 resolved_hop
->flags
|= NEXTHOP_FLAG_ONLINK
;
1664 if (afi
== AFI_IP
) {
1665 resolved_hop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
1666 resolved_hop
->gate
.ipv4
= nexthop
->gate
.ipv4
;
1667 } else if (afi
== AFI_IP6
) {
1668 resolved_hop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
1669 resolved_hop
->gate
.ipv6
= nexthop
->gate
.ipv6
;
1671 resolved_hop
->ifindex
= newhop
->ifindex
;
1673 case NEXTHOP_TYPE_BLACKHOLE
:
1674 resolved_hop
->type
= NEXTHOP_TYPE_BLACKHOLE
;
1675 resolved_hop
->bh_type
= newhop
->bh_type
;
1679 if (newhop
->flags
& NEXTHOP_FLAG_ONLINK
)
1680 resolved_hop
->flags
|= NEXTHOP_FLAG_ONLINK
;
1682 /* Copy labels of the resolved route and the parent resolving to it */
1687 * Don't push the first SID if the corresponding action in the
1690 if (!newhop
->nh_label
|| !newhop
->nh_label
->num_labels
1691 || newhop
->nh_label
->label
[0] == MPLS_LABEL_IMPLICIT_NULL
)
1694 for (; i
< policy
->segment_list
.label_num
; i
++)
1695 labels
[num_labels
++] = policy
->segment_list
.labels
[i
];
1696 label_type
= policy
->segment_list
.type
;
1697 } else if (newhop
->nh_label
) {
1698 for (i
= 0; i
< newhop
->nh_label
->num_labels
; i
++) {
1699 /* Be a bit picky about overrunning the local array */
1700 if (num_labels
>= MPLS_MAX_LABELS
) {
1701 if (IS_ZEBRA_DEBUG_NHG
|| IS_ZEBRA_DEBUG_RIB
)
1702 zlog_debug("%s: too many labels in newhop %pNHv",
1706 labels
[num_labels
++] = newhop
->nh_label
->label
[i
];
1708 /* Use the "outer" type */
1709 label_type
= newhop
->nh_label_type
;
1712 if (nexthop
->nh_label
) {
1713 for (i
= 0; i
< nexthop
->nh_label
->num_labels
; i
++) {
1714 /* Be a bit picky about overrunning the local array */
1715 if (num_labels
>= MPLS_MAX_LABELS
) {
1716 if (IS_ZEBRA_DEBUG_NHG
|| IS_ZEBRA_DEBUG_RIB
)
1717 zlog_debug("%s: too many labels in nexthop %pNHv",
1721 labels
[num_labels
++] = nexthop
->nh_label
->label
[i
];
1724 /* If the parent has labels, use its type if
1725 * we don't already have one.
1727 if (label_type
== ZEBRA_LSP_NONE
)
1728 label_type
= nexthop
->nh_label_type
;
1732 nexthop_add_labels(resolved_hop
, label_type
, num_labels
,
1735 resolved_hop
->rparent
= nexthop
;
1736 _nexthop_add(&nexthop
->resolved
, resolved_hop
);
1739 /* Checks if nexthop we are trying to resolve to is valid */
1740 static bool nexthop_valid_resolve(const struct nexthop
*nexthop
,
1741 const struct nexthop
*resolved
)
1743 /* Can't resolve to a recursive nexthop */
1744 if (CHECK_FLAG(resolved
->flags
, NEXTHOP_FLAG_RECURSIVE
))
1747 /* Must be ACTIVE */
1748 if (!CHECK_FLAG(resolved
->flags
, NEXTHOP_FLAG_ACTIVE
))
1751 switch (nexthop
->type
) {
1752 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1753 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1754 /* If the nexthop we are resolving to does not match the
1755 * ifindex for the nexthop the route wanted, its not valid.
1757 if (nexthop
->ifindex
!= resolved
->ifindex
)
1760 case NEXTHOP_TYPE_IPV4
:
1761 case NEXTHOP_TYPE_IPV6
:
1762 case NEXTHOP_TYPE_IFINDEX
:
1763 case NEXTHOP_TYPE_BLACKHOLE
:
1771 * Given a nexthop we need to properly recursively resolve
1772 * the route. As such, do a table lookup to find and match
1773 * if at all possible. Set the nexthop->ifindex and resolved_id
1776 static int nexthop_active(afi_t afi
, struct route_entry
*re
,
1777 struct nexthop
*nexthop
, struct route_node
*top
)
1780 struct route_table
*table
;
1781 struct route_node
*rn
;
1782 struct route_entry
*match
= NULL
;
1784 zebra_nhlfe_t
*nhlfe
;
1785 struct nexthop
*newhop
;
1786 struct interface
*ifp
;
1788 struct zebra_vrf
*zvrf
;
1789 struct in_addr local_ipv4
;
1790 struct in_addr
*ipv4
;
1792 if ((nexthop
->type
== NEXTHOP_TYPE_IPV4
)
1793 || nexthop
->type
== NEXTHOP_TYPE_IPV6
)
1794 nexthop
->ifindex
= 0;
1797 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_RECURSIVE
);
1798 nexthops_free(nexthop
->resolved
);
1799 nexthop
->resolved
= NULL
;
1800 re
->nexthop_mtu
= 0;
1802 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
1803 zlog_debug("%s: re %p, nexthop %pNHv",
1804 __func__
, re
, nexthop
);
1807 * If the kernel has sent us a NEW route, then
1808 * by golly gee whiz it's a good route.
1810 * If its an already INSTALLED route we have already handled, then the
1811 * kernel route's nexthop might have became unreachable
1812 * and we have to handle that.
1814 if (!CHECK_FLAG(re
->status
, ROUTE_ENTRY_INSTALLED
)
1815 && (re
->type
== ZEBRA_ROUTE_KERNEL
1816 || re
->type
== ZEBRA_ROUTE_SYSTEM
))
1820 * If the nexthop has been marked as 'onlink' we just need to make
1821 * sure the nexthop's interface is known and is operational.
1823 if (CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ONLINK
)) {
1824 ifp
= if_lookup_by_index(nexthop
->ifindex
, nexthop
->vrf_id
);
1826 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
1827 zlog_debug("nexthop %pNHv marked onlink but nhif %u doesn't exist",
1828 nexthop
, nexthop
->ifindex
);
1831 if (!if_is_operative(ifp
)) {
1832 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
1833 zlog_debug("nexthop %pNHv marked onlink but nhif %s is not operational",
1834 nexthop
, ifp
->name
);
1840 if ((top
->p
.family
== AF_INET
&& top
->p
.prefixlen
== 32
1841 && nexthop
->gate
.ipv4
.s_addr
== top
->p
.u
.prefix4
.s_addr
)
1842 || (top
->p
.family
== AF_INET6
&& top
->p
.prefixlen
== 128
1843 && memcmp(&nexthop
->gate
.ipv6
, &top
->p
.u
.prefix6
, 16) == 0)) {
1844 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
1846 " :%s: Attempting to install a max prefixlength route through itself",
1851 /* Validation for ipv4 mapped ipv6 nexthop. */
1852 if (IS_MAPPED_IPV6(&nexthop
->gate
.ipv6
)) {
1855 ipv4_mapped_ipv6_to_ipv4(&nexthop
->gate
.ipv6
, ipv4
);
1857 ipv4
= &nexthop
->gate
.ipv4
;
1860 if (nexthop
->srte_color
) {
1861 struct ipaddr endpoint
= {0};
1862 struct zebra_sr_policy
*policy
;
1866 endpoint
.ipa_type
= IPADDR_V4
;
1867 endpoint
.ipaddr_v4
= *ipv4
;
1870 endpoint
.ipa_type
= IPADDR_V6
;
1871 endpoint
.ipaddr_v6
= nexthop
->gate
.ipv6
;
1874 flog_err(EC_LIB_DEVELOPMENT
,
1875 "%s: unknown address-family: %u", __func__
,
1880 policy
= zebra_sr_policy_find(nexthop
->srte_color
, &endpoint
);
1881 if (policy
&& policy
->status
== ZEBRA_SR_POLICY_UP
) {
1883 frr_each_safe (nhlfe_list
, &policy
->lsp
->nhlfe_list
,
1885 if (!CHECK_FLAG(nhlfe
->flags
,
1886 NHLFE_FLAG_SELECTED
)
1887 || CHECK_FLAG(nhlfe
->flags
,
1888 NHLFE_FLAG_DELETED
))
1890 SET_FLAG(nexthop
->flags
,
1891 NEXTHOP_FLAG_RECURSIVE
);
1892 nexthop_set_resolved(afi
, nhlfe
->nexthop
,
1901 /* Make lookup prefix. */
1902 memset(&p
, 0, sizeof(struct prefix
));
1906 p
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1907 p
.u
.prefix4
= *ipv4
;
1910 p
.family
= AF_INET6
;
1911 p
.prefixlen
= IPV6_MAX_PREFIXLEN
;
1912 p
.u
.prefix6
= nexthop
->gate
.ipv6
;
1915 assert(afi
!= AFI_IP
&& afi
!= AFI_IP6
);
1919 table
= zebra_vrf_table(afi
, SAFI_UNICAST
, nexthop
->vrf_id
);
1921 zvrf
= zebra_vrf_lookup_by_id(nexthop
->vrf_id
);
1922 if (!table
|| !zvrf
) {
1923 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
1924 zlog_debug(" %s: Table not found", __func__
);
1928 rn
= route_node_match(table
, (struct prefix
*)&p
);
1930 route_unlock_node(rn
);
1932 /* Lookup should halt if we've matched against ourselves ('top',
1933 * if specified) - i.e., we cannot have a nexthop NH1 is
1934 * resolved by a route NH1. The exception is if the route is a
1938 if (((afi
== AFI_IP
) && (rn
->p
.prefixlen
!= 32))
1939 || ((afi
== AFI_IP6
) && (rn
->p
.prefixlen
!= 128))) {
1940 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
1942 " %s: Matched against ourself and prefix length is not max bit length",
1947 /* Pick up selected route. */
1948 /* However, do not resolve over default route unless explicitly
1951 if (is_default_prefix(&rn
->p
)
1952 && !rnh_resolve_via_default(zvrf
, p
.family
)) {
1953 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
1955 " :%s: Resolved against default route",
1960 dest
= rib_dest_from_rnode(rn
);
1961 if (dest
&& dest
->selected_fib
1962 && !CHECK_FLAG(dest
->selected_fib
->status
,
1963 ROUTE_ENTRY_REMOVED
)
1964 && dest
->selected_fib
->type
!= ZEBRA_ROUTE_TABLE
)
1965 match
= dest
->selected_fib
;
1967 /* If there is no selected route or matched route is EGP, go up
1973 } while (rn
&& rn
->info
== NULL
);
1975 route_lock_node(rn
);
1980 if (match
->type
== ZEBRA_ROUTE_CONNECT
) {
1981 /* Directly point connected route. */
1982 newhop
= match
->nhe
->nhg
.nexthop
;
1984 if (nexthop
->type
== NEXTHOP_TYPE_IPV4
1985 || nexthop
->type
== NEXTHOP_TYPE_IPV6
)
1986 nexthop
->ifindex
= newhop
->ifindex
;
1987 else if (nexthop
->ifindex
!= newhop
->ifindex
) {
1988 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
1990 "%s: %pNHv given ifindex does not match nexthops ifindex found found: %pNHv",
1994 * NEXTHOP_TYPE_*_IFINDEX but ifindex
1995 * doesn't match what we found.
2001 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2002 zlog_debug("%s: CONNECT match %p (%u), newhop %pNHv",
2004 match
->nhe
->id
, newhop
);
2007 } else if (CHECK_FLAG(re
->flags
, ZEBRA_FLAG_ALLOW_RECURSION
)) {
2008 struct nexthop_group
*nhg
;
2012 /* Only useful if installed */
2013 if (!CHECK_FLAG(match
->status
, ROUTE_ENTRY_INSTALLED
)) {
2014 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
2015 zlog_debug("%s: match %p (%u) not installed",
2019 goto done_with_match
;
2022 /* Examine installed nexthops; note that there
2023 * may not be any installed primary nexthops if
2024 * only backups are installed.
2026 nhg
= rib_get_fib_nhg(match
);
2027 for (ALL_NEXTHOPS_PTR(nhg
, newhop
)) {
2028 if (!nexthop_valid_resolve(nexthop
, newhop
))
2031 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2032 zlog_debug("%s: RECURSIVE match %p (%u), newhop %pNHv",
2034 match
->nhe
->id
, newhop
);
2036 SET_FLAG(nexthop
->flags
,
2037 NEXTHOP_FLAG_RECURSIVE
);
2038 nexthop_set_resolved(afi
, newhop
, nexthop
,
2043 /* Examine installed backup nexthops, if any. There
2044 * are only installed backups *if* there is a
2045 * dedicated fib list.
2047 nhg
= rib_get_fib_backup_nhg(match
);
2048 if (nhg
== NULL
|| nhg
->nexthop
== NULL
)
2049 goto done_with_match
;
2051 for (ALL_NEXTHOPS_PTR(nhg
, newhop
)) {
2052 if (!nexthop_valid_resolve(nexthop
, newhop
))
2055 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2056 zlog_debug("%s: RECURSIVE match backup %p (%u), newhop %pNHv",
2058 match
->nhe
->id
, newhop
);
2060 SET_FLAG(nexthop
->flags
,
2061 NEXTHOP_FLAG_RECURSIVE
);
2062 nexthop_set_resolved(afi
, newhop
, nexthop
,
2068 re
->nexthop_mtu
= match
->mtu
;
2069 else if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
2071 " %s: Recursion failed to find",
2076 if (IS_ZEBRA_DEBUG_RIB_DETAILED
) {
2078 " %s: Route Type %s has not turned on recursion",
2079 __func__
, zebra_route_string(re
->type
));
2080 if (re
->type
== ZEBRA_ROUTE_BGP
2081 && !CHECK_FLAG(re
->flags
, ZEBRA_FLAG_IBGP
))
2083 " EBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
2088 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
2089 zlog_debug(" %s: Nexthop did not lookup in table",
2094 /* This function verifies reachability of one given nexthop, which can be
2095 * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
2096 * in nexthop->flags field. The nexthop->ifindex will be updated
2097 * appropriately as well. An existing route map can turn
2098 * (otherwise active) nexthop into inactive, but not vice versa.
2100 * If it finds a nexthop recursivedly, set the resolved_id
2101 * to match that nexthop's nhg_hash_entry ID;
2103 * The return value is the final value of 'ACTIVE' flag.
2105 static unsigned nexthop_active_check(struct route_node
*rn
,
2106 struct route_entry
*re
,
2107 struct nexthop
*nexthop
)
2109 struct interface
*ifp
;
2110 route_map_result_t ret
= RMAP_PERMITMATCH
;
2112 char buf
[SRCDEST2STR_BUFFER
];
2113 const struct prefix
*p
, *src_p
;
2114 struct zebra_vrf
*zvrf
;
2116 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
2118 if (rn
->p
.family
== AF_INET
)
2120 else if (rn
->p
.family
== AF_INET6
)
2124 switch (nexthop
->type
) {
2125 case NEXTHOP_TYPE_IFINDEX
:
2126 ifp
= if_lookup_by_index(nexthop
->ifindex
, nexthop
->vrf_id
);
2128 * If the interface exists and its operative or its a kernel
2129 * route and interface is up, its active. We trust kernel routes
2133 && (if_is_operative(ifp
)
2135 && (re
->type
== ZEBRA_ROUTE_KERNEL
2136 || re
->type
== ZEBRA_ROUTE_SYSTEM
))))
2137 SET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2139 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2141 case NEXTHOP_TYPE_IPV4
:
2142 case NEXTHOP_TYPE_IPV4_IFINDEX
:
2144 if (nexthop_active(AFI_IP
, re
, nexthop
, rn
))
2145 SET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2147 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2149 case NEXTHOP_TYPE_IPV6
:
2151 if (nexthop_active(AFI_IP6
, re
, nexthop
, rn
))
2152 SET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2154 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2156 case NEXTHOP_TYPE_IPV6_IFINDEX
:
2157 /* RFC 5549, v4 prefix with v6 NH */
2158 if (rn
->p
.family
!= AF_INET
)
2160 if (IN6_IS_ADDR_LINKLOCAL(&nexthop
->gate
.ipv6
)) {
2161 ifp
= if_lookup_by_index(nexthop
->ifindex
,
2163 if (ifp
&& if_is_operative(ifp
))
2164 SET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2166 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2168 if (nexthop_active(AFI_IP6
, re
, nexthop
, rn
))
2169 SET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2171 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2174 case NEXTHOP_TYPE_BLACKHOLE
:
2175 SET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2181 if (!CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
)) {
2182 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
2183 zlog_debug(" %s: Unable to find active nexthop",
2188 /* XXX: What exactly do those checks do? Do we support
2189 * e.g. IPv4 routes with IPv6 nexthops or vice versa?
2191 if (RIB_SYSTEM_ROUTE(re
) || (family
== AFI_IP
&& p
->family
!= AF_INET
)
2192 || (family
== AFI_IP6
&& p
->family
!= AF_INET6
))
2193 return CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2195 /* The original code didn't determine the family correctly
2196 * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
2197 * from the rib_table_info in those cases.
2198 * Possibly it may be better to use only the rib_table_info
2202 struct rib_table_info
*info
;
2204 info
= srcdest_rnode_table_info(rn
);
2208 memset(&nexthop
->rmap_src
.ipv6
, 0, sizeof(union g_addr
));
2210 zvrf
= zebra_vrf_lookup_by_id(nexthop
->vrf_id
);
2212 if (IS_ZEBRA_DEBUG_RIB_DETAILED
)
2213 zlog_debug(" %s: zvrf is NULL", __func__
);
2214 return CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2217 /* It'll get set if required inside */
2218 ret
= zebra_route_map_check(family
, re
->type
, re
->instance
, p
, nexthop
,
2220 if (ret
== RMAP_DENYMATCH
) {
2221 if (IS_ZEBRA_DEBUG_RIB
) {
2222 srcdest_rnode2str(rn
, buf
, sizeof(buf
));
2224 "%u:%s: Filtering out with NH out %s due to route map",
2226 ifindex2ifname(nexthop
->ifindex
,
2229 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2231 return CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2234 /* Helper function called after resolution to walk nhg rb trees
2235 * and toggle the NEXTHOP_GROUP_VALID flag if the nexthop
2236 * is active on singleton NHEs.
2238 static bool zebra_nhg_set_valid_if_active(struct nhg_hash_entry
*nhe
)
2240 struct nhg_connected
*rb_node_dep
= NULL
;
2243 if (!zebra_nhg_depends_is_empty(nhe
)) {
2244 /* Is at least one depend valid? */
2245 frr_each(nhg_connected_tree
, &nhe
->nhg_depends
, rb_node_dep
) {
2246 if (zebra_nhg_set_valid_if_active(rb_node_dep
->nhe
))
2253 /* should be fully resolved singleton at this point */
2254 if (CHECK_FLAG(nhe
->nhg
.nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
))
2259 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_VALID
);
2265 * Process a list of nexthops, given an nhg, determining
2266 * whether each one is ACTIVE/installable at this time.
2268 static uint32_t nexthop_list_active_update(struct route_node
*rn
,
2269 struct route_entry
*re
,
2270 struct nexthop_group
*nhg
)
2272 union g_addr prev_src
;
2273 unsigned int prev_active
, new_active
;
2274 ifindex_t prev_index
;
2275 uint32_t counter
= 0;
2276 struct nexthop
*nexthop
;
2278 nexthop
= nhg
->nexthop
;
2280 /* Process nexthops one-by-one */
2281 for ( ; nexthop
; nexthop
= nexthop
->next
) {
2283 /* No protocol daemon provides src and so we're skipping
2286 prev_src
= nexthop
->rmap_src
;
2287 prev_active
= CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2288 prev_index
= nexthop
->ifindex
;
2290 * We need to respect the multipath_num here
2291 * as that what we should be able to install from
2292 * a multipath perspective should not be a data plane
2296 nexthop_active_check(rn
, re
, nexthop
);
2298 if (new_active
&& counter
>= zrouter
.multipath_num
) {
2301 /* Set it and its resolved nexthop as inactive. */
2302 for (nh
= nexthop
; nh
; nh
= nh
->resolved
)
2303 UNSET_FLAG(nh
->flags
, NEXTHOP_FLAG_ACTIVE
);
2311 /* Don't allow src setting on IPv6 addr for now */
2312 if (prev_active
!= new_active
|| prev_index
!= nexthop
->ifindex
2313 || ((nexthop
->type
>= NEXTHOP_TYPE_IFINDEX
2314 && nexthop
->type
< NEXTHOP_TYPE_IPV6
)
2315 && prev_src
.ipv4
.s_addr
2316 != nexthop
->rmap_src
.ipv4
.s_addr
)
2317 || ((nexthop
->type
>= NEXTHOP_TYPE_IPV6
2318 && nexthop
->type
< NEXTHOP_TYPE_BLACKHOLE
)
2319 && !(IPV6_ADDR_SAME(&prev_src
.ipv6
,
2320 &nexthop
->rmap_src
.ipv6
)))
2321 || CHECK_FLAG(re
->status
, ROUTE_ENTRY_LABELS_CHANGED
))
2322 SET_FLAG(re
->status
, ROUTE_ENTRY_CHANGED
);
2329 static uint32_t proto_nhg_nexthop_active_update(struct nexthop_group
*nhg
)
2332 uint32_t curr_active
= 0;
2334 /* Assume all active for now */
2336 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
) {
2337 SET_FLAG(nh
->flags
, NEXTHOP_FLAG_ACTIVE
);
2345 * Iterate over all nexthops of the given RIB entry and refresh their
2346 * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag,
2347 * the whole re structure is flagged with ROUTE_ENTRY_CHANGED.
2349 * Return value is the new number of active nexthops.
2351 int nexthop_active_update(struct route_node
*rn
, struct route_entry
*re
)
2353 struct nhg_hash_entry
*curr_nhe
;
2354 uint32_t curr_active
= 0, backup_active
= 0;
2356 if (re
->nhe
->id
>= ZEBRA_NHG_PROTO_LOWER
)
2357 return proto_nhg_nexthop_active_update(&re
->nhe
->nhg
);
2359 afi_t rt_afi
= family2afi(rn
->p
.family
);
2361 UNSET_FLAG(re
->status
, ROUTE_ENTRY_CHANGED
);
2363 /* Make a local copy of the existing nhe, so we don't work on/modify
2366 curr_nhe
= zebra_nhe_copy(re
->nhe
, re
->nhe
->id
);
2368 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2369 zlog_debug("%s: re %p nhe %p (%u), curr_nhe %p",
2370 __func__
, re
, re
->nhe
, re
->nhe
->id
,
2373 /* Clear the existing id, if any: this will avoid any confusion
2374 * if the id exists, and will also force the creation
2375 * of a new nhe reflecting the changes we may make in this local copy.
2379 /* Process nexthops */
2380 curr_active
= nexthop_list_active_update(rn
, re
, &curr_nhe
->nhg
);
2382 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2383 zlog_debug("%s: re %p curr_active %u", __func__
, re
,
2386 /* If there are no backup nexthops, we are done */
2387 if (zebra_nhg_get_backup_nhg(curr_nhe
) == NULL
)
2390 backup_active
= nexthop_list_active_update(
2391 rn
, re
, zebra_nhg_get_backup_nhg(curr_nhe
));
2393 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2394 zlog_debug("%s: re %p backup_active %u", __func__
, re
,
2400 * Ref or create an nhe that matches the current state of the
2403 if (CHECK_FLAG(re
->status
, ROUTE_ENTRY_CHANGED
)) {
2404 struct nhg_hash_entry
*new_nhe
= NULL
;
2406 new_nhe
= zebra_nhg_rib_find_nhe(curr_nhe
, rt_afi
);
2408 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2409 zlog_debug("%s: re %p CHANGED: nhe %p (%u) => new_nhe %p (%u)",
2410 __func__
, re
, re
->nhe
,
2411 re
->nhe
->id
, new_nhe
, new_nhe
->id
);
2413 route_entry_update_nhe(re
, new_nhe
);
2417 /* Walk the NHE depends tree and toggle NEXTHOP_GROUP_VALID
2418 * flag where appropriate.
2421 zebra_nhg_set_valid_if_active(re
->nhe
);
2424 * Do not need the old / copied nhe anymore since it
2425 * was either copied over into a new nhe or not
2428 zebra_nhg_free(curr_nhe
);
2432 /* Recursively construct a grp array of fully resolved IDs.
2434 * This function allows us to account for groups within groups,
2435 * by converting them into a flat array of IDs.
2437 * nh_grp is modified at every level of recursion to append
2438 * to it the next unique, fully resolved ID from the entire tree.
2442 * I'm pretty sure we only allow ONE level of group within group currently.
2443 * But making this recursive just in case that ever changes.
2445 static uint8_t zebra_nhg_nhe2grp_internal(struct nh_grp
*grp
,
2447 struct nhg_hash_entry
*nhe
,
2450 struct nhg_connected
*rb_node_dep
= NULL
;
2451 struct nhg_hash_entry
*depend
= NULL
;
2452 uint8_t i
= curr_index
;
2454 frr_each(nhg_connected_tree
, &nhe
->nhg_depends
, rb_node_dep
) {
2455 bool duplicate
= false;
2460 depend
= rb_node_dep
->nhe
;
2463 * If its recursive, use its resolved nhe in the group
2465 if (CHECK_FLAG(depend
->flags
, NEXTHOP_GROUP_RECURSIVE
)) {
2466 depend
= zebra_nhg_resolve(depend
);
2469 EC_ZEBRA_NHG_FIB_UPDATE
,
2470 "Failed to recursively resolve Nexthop Hash Entry in the group id=%u",
2476 if (!zebra_nhg_depends_is_empty(depend
)) {
2477 /* This is a group within a group */
2478 i
= zebra_nhg_nhe2grp_internal(grp
, i
, depend
, max_num
);
2480 if (!CHECK_FLAG(depend
->flags
, NEXTHOP_GROUP_VALID
)) {
2481 if (IS_ZEBRA_DEBUG_RIB_DETAILED
2482 || IS_ZEBRA_DEBUG_NHG
)
2484 "%s: Nexthop ID (%u) not valid, not appending to dataplane install group",
2485 __func__
, depend
->id
);
2489 /* If the nexthop not installed/queued for install don't
2490 * put in the ID array.
2492 if (!(CHECK_FLAG(depend
->flags
, NEXTHOP_GROUP_INSTALLED
)
2493 || CHECK_FLAG(depend
->flags
,
2494 NEXTHOP_GROUP_QUEUED
))) {
2495 if (IS_ZEBRA_DEBUG_RIB_DETAILED
2496 || IS_ZEBRA_DEBUG_NHG
)
2498 "%s: Nexthop ID (%u) not installed or queued for install, not appending to dataplane install group",
2499 __func__
, depend
->id
);
2503 /* Check for duplicate IDs, ignore if found. */
2504 for (int j
= 0; j
< i
; j
++) {
2505 if (depend
->id
== grp
[j
].id
) {
2512 if (IS_ZEBRA_DEBUG_RIB_DETAILED
2513 || IS_ZEBRA_DEBUG_NHG
)
2515 "%s: Nexthop ID (%u) is duplicate, not appending to dataplane install group",
2516 __func__
, depend
->id
);
2520 grp
[i
].id
= depend
->id
;
2521 grp
[i
].weight
= depend
->nhg
.nexthop
->weight
;
2526 if (nhe
->backup_info
== NULL
|| nhe
->backup_info
->nhe
== NULL
)
2529 /* TODO -- For now, we are not trying to use or install any
2530 * backup info in this nexthop-id path: we aren't prepared
2531 * to use the backups here yet. We're just debugging what we find.
2533 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2534 zlog_debug("%s: skipping backup nhe", __func__
);
2540 /* Convert a nhe into a group array */
2541 uint8_t zebra_nhg_nhe2grp(struct nh_grp
*grp
, struct nhg_hash_entry
*nhe
,
2544 /* Call into the recursive function */
2545 return zebra_nhg_nhe2grp_internal(grp
, 0, nhe
, max_num
);
2548 void zebra_nhg_install_kernel(struct nhg_hash_entry
*nhe
)
2550 struct nhg_connected
*rb_node_dep
= NULL
;
2552 /* Resolve it first */
2553 nhe
= zebra_nhg_resolve(nhe
);
2555 /* Make sure all depends are installed/queued */
2556 frr_each(nhg_connected_tree
, &nhe
->nhg_depends
, rb_node_dep
) {
2557 zebra_nhg_install_kernel(rb_node_dep
->nhe
);
2560 if (CHECK_FLAG(nhe
->flags
, NEXTHOP_GROUP_VALID
)
2561 && !CHECK_FLAG(nhe
->flags
, NEXTHOP_GROUP_INSTALLED
)
2562 && !CHECK_FLAG(nhe
->flags
, NEXTHOP_GROUP_QUEUED
)) {
2563 /* Change its type to us since we are installing it */
2564 if (!ZEBRA_NHG_CREATED(nhe
))
2565 nhe
->type
= ZEBRA_ROUTE_NHG
;
2567 int ret
= dplane_nexthop_add(nhe
);
2570 case ZEBRA_DPLANE_REQUEST_QUEUED
:
2571 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_QUEUED
);
2573 case ZEBRA_DPLANE_REQUEST_FAILURE
:
2575 EC_ZEBRA_DP_INSTALL_FAIL
,
2576 "Failed to install Nexthop ID (%u) into the kernel",
2579 case ZEBRA_DPLANE_REQUEST_SUCCESS
:
2580 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_INSTALLED
);
2581 zebra_nhg_handle_install(nhe
);
2587 void zebra_nhg_uninstall_kernel(struct nhg_hash_entry
*nhe
)
2589 if (CHECK_FLAG(nhe
->flags
, NEXTHOP_GROUP_INSTALLED
)) {
2590 int ret
= dplane_nexthop_delete(nhe
);
2593 case ZEBRA_DPLANE_REQUEST_QUEUED
:
2594 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_QUEUED
);
2596 case ZEBRA_DPLANE_REQUEST_FAILURE
:
2598 EC_ZEBRA_DP_DELETE_FAIL
,
2599 "Failed to uninstall Nexthop ID (%u) from the kernel",
2602 case ZEBRA_DPLANE_REQUEST_SUCCESS
:
2603 UNSET_FLAG(nhe
->flags
, NEXTHOP_GROUP_INSTALLED
);
2608 zebra_nhg_handle_uninstall(nhe
);
2611 void zebra_nhg_dplane_result(struct zebra_dplane_ctx
*ctx
)
2613 enum dplane_op_e op
;
2614 enum zebra_dplane_result status
;
2616 struct nhg_hash_entry
*nhe
= NULL
;
2618 op
= dplane_ctx_get_op(ctx
);
2619 status
= dplane_ctx_get_status(ctx
);
2621 id
= dplane_ctx_get_nhe_id(ctx
);
2623 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL
|| IS_ZEBRA_DEBUG_NHG_DETAIL
)
2625 "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s",
2626 ctx
, dplane_op2str(op
), id
, dplane_res2str(status
));
2629 case DPLANE_OP_NH_DELETE
:
2630 if (status
!= ZEBRA_DPLANE_REQUEST_SUCCESS
)
2632 EC_ZEBRA_DP_DELETE_FAIL
,
2633 "Failed to uninstall Nexthop ID (%u) from the kernel",
2635 /* We already free'd the data, nothing to do */
2637 case DPLANE_OP_NH_INSTALL
:
2638 case DPLANE_OP_NH_UPDATE
:
2639 nhe
= zebra_nhg_lookup_id(id
);
2644 "%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table",
2645 dplane_op2str(op
), id
);
2649 UNSET_FLAG(nhe
->flags
, NEXTHOP_GROUP_QUEUED
);
2650 if (status
== ZEBRA_DPLANE_REQUEST_SUCCESS
) {
2651 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_VALID
);
2652 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_INSTALLED
);
2653 zebra_nhg_handle_install(nhe
);
2656 EC_ZEBRA_DP_INSTALL_FAIL
,
2657 "Failed to install Nexthop ID (%u) into the kernel",
2660 case DPLANE_OP_ROUTE_INSTALL
:
2661 case DPLANE_OP_ROUTE_UPDATE
:
2662 case DPLANE_OP_ROUTE_DELETE
:
2663 case DPLANE_OP_ROUTE_NOTIFY
:
2664 case DPLANE_OP_LSP_INSTALL
:
2665 case DPLANE_OP_LSP_UPDATE
:
2666 case DPLANE_OP_LSP_DELETE
:
2667 case DPLANE_OP_LSP_NOTIFY
:
2668 case DPLANE_OP_PW_INSTALL
:
2669 case DPLANE_OP_PW_UNINSTALL
:
2670 case DPLANE_OP_SYS_ROUTE_ADD
:
2671 case DPLANE_OP_SYS_ROUTE_DELETE
:
2672 case DPLANE_OP_ADDR_INSTALL
:
2673 case DPLANE_OP_ADDR_UNINSTALL
:
2674 case DPLANE_OP_MAC_INSTALL
:
2675 case DPLANE_OP_MAC_DELETE
:
2676 case DPLANE_OP_NEIGH_INSTALL
:
2677 case DPLANE_OP_NEIGH_UPDATE
:
2678 case DPLANE_OP_NEIGH_DELETE
:
2679 case DPLANE_OP_VTEP_ADD
:
2680 case DPLANE_OP_VTEP_DELETE
:
2681 case DPLANE_OP_RULE_ADD
:
2682 case DPLANE_OP_RULE_DELETE
:
2683 case DPLANE_OP_RULE_UPDATE
:
2684 case DPLANE_OP_NEIGH_DISCOVER
:
2685 case DPLANE_OP_NONE
:
2689 dplane_ctx_fini(&ctx
);
2692 static void zebra_nhg_sweep_entry(struct hash_bucket
*bucket
, void *arg
)
2694 struct nhg_hash_entry
*nhe
= NULL
;
2696 nhe
= (struct nhg_hash_entry
*)bucket
->data
;
2698 /* If its being ref'd, just let it be uninstalled via a route removal */
2699 if (ZEBRA_NHG_CREATED(nhe
) && nhe
->refcnt
<= 0)
2700 zebra_nhg_uninstall_kernel(nhe
);
2703 void zebra_nhg_sweep_table(struct hash
*hash
)
2705 hash_iterate(hash
, zebra_nhg_sweep_entry
, NULL
);
2708 /* Global control to disable use of kernel nexthops, if available. We can't
2709 * force the kernel to support nexthop ids, of course, but we can disable
2710 * zebra's use of them, for testing e.g. By default, if the kernel supports
2711 * nexthop ids, zebra uses them.
2713 void zebra_nhg_enable_kernel_nexthops(bool set
)
2715 g_nexthops_enabled
= set
;
2718 bool zebra_nhg_kernel_nexthops_enabled(void)
2720 return g_nexthops_enabled
;
2724 * Global control to only use kernel nexthops for protocol created NHGs.
2725 * There are some use cases where you may not want zebra to implicitly
2726 * create kernel nexthops for all routes and only create them for NHGs
2727 * passed down by upper level protos.
2731 void zebra_nhg_set_proto_nexthops_only(bool set
)
2733 proto_nexthops_only
= set
;
2736 bool zebra_nhg_proto_nexthops_only(void)
2738 return proto_nexthops_only
;
2741 /* Add NHE from upper level proto */
2742 struct nhg_hash_entry
*zebra_nhg_proto_add(uint32_t id
, int type
,
2743 struct nexthop_group
*nhg
, afi_t afi
)
2745 struct nhg_hash_entry lookup
;
2746 struct nhg_hash_entry
*new, *old
;
2747 struct nhg_connected
*rb_node_dep
= NULL
;
2748 struct nexthop
*newhop
;
2749 bool replace
= false;
2751 if (!nhg
->nexthop
) {
2752 if (IS_ZEBRA_DEBUG_NHG
)
2753 zlog_debug("%s: id %u, no nexthops passed to add",
2759 /* Set nexthop list as active, since they wont go through rib
2762 * Assuming valid/onlink for now.
2764 * Once resolution is figured out, we won't need this!
2766 for (ALL_NEXTHOPS_PTR(nhg
, newhop
)) {
2767 if (CHECK_FLAG(newhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
2768 if (IS_ZEBRA_DEBUG_NHG
)
2770 "%s: id %u, backup nexthops not supported",
2775 if (newhop
->type
== NEXTHOP_TYPE_BLACKHOLE
) {
2776 if (IS_ZEBRA_DEBUG_NHG
)
2778 "%s: id %u, blackhole nexthop not supported",
2783 if (newhop
->type
== NEXTHOP_TYPE_IFINDEX
) {
2784 if (IS_ZEBRA_DEBUG_NHG
)
2786 "%s: id %u, nexthop without gateway not supported",
2791 if (!newhop
->ifindex
) {
2792 if (IS_ZEBRA_DEBUG_NHG
)
2794 "%s: id %u, nexthop without ifindex is not supported",
2798 SET_FLAG(newhop
->flags
, NEXTHOP_FLAG_ACTIVE
);
2801 zebra_nhe_init(&lookup
, afi
, nhg
->nexthop
);
2802 lookup
.nhg
.nexthop
= nhg
->nexthop
;
2806 old
= zebra_nhg_lookup_id(id
);
2810 * This is a replace, just release NHE from ID for now, The
2811 * depends/dependents may still be used in the replacement.
2814 hash_release(zrouter
.nhgs_id
, old
);
2817 new = zebra_nhg_rib_find_nhe(&lookup
, afi
);
2819 zebra_nhg_increment_ref(new);
2821 zebra_nhg_set_valid_if_active(new);
2823 zebra_nhg_install_kernel(new);
2827 * Check to handle recving DEL while routes still in use then
2830 * In this case we would have decremented the refcnt already
2831 * but set the FLAG here. Go ahead and increment once to fix
2832 * the misordering we have been sent.
2834 if (CHECK_FLAG(old
->flags
, NEXTHOP_GROUP_PROTO_RELEASED
))
2835 zebra_nhg_increment_ref(old
);
2837 rib_handle_nhg_replace(old
, new);
2839 /* if this != 1 at this point, we have a bug */
2840 assert(old
->refcnt
== 1);
2842 /* We have to decrement its singletons
2843 * because some might not exist in NEW.
2845 if (!zebra_nhg_depends_is_empty(old
)) {
2846 frr_each (nhg_connected_tree
, &old
->nhg_depends
,
2848 zebra_nhg_decrement_ref(rb_node_dep
->nhe
);
2851 /* Free all the things */
2852 zebra_nhg_release_all_deps(old
);
2854 /* Dont call the dec API, we dont want to uninstall the ID */
2856 zebra_nhg_free(old
);
2860 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2861 zlog_debug("%s: %s nhe %p (%u), vrf %d, type %s", __func__
,
2862 (replace
? "replaced" : "added"), new, new->id
,
2863 new->vrf_id
, zebra_route_string(new->type
));
2868 /* Delete NHE from upper level proto, caller must decrement ref */
2869 struct nhg_hash_entry
*zebra_nhg_proto_del(uint32_t id
, int type
)
2871 struct nhg_hash_entry
*nhe
;
2873 nhe
= zebra_nhg_lookup_id(id
);
2876 if (IS_ZEBRA_DEBUG_NHG
)
2877 zlog_debug("%s: id %u, lookup failed", __func__
, id
);
2882 if (type
!= nhe
->type
) {
2883 if (IS_ZEBRA_DEBUG_NHG
)
2885 "%s: id %u, type %s mismatch, sent by %s, ignoring",
2886 __func__
, id
, zebra_route_string(nhe
->type
),
2887 zebra_route_string(type
));
2891 if (CHECK_FLAG(nhe
->flags
, NEXTHOP_GROUP_PROTO_RELEASED
)) {
2892 if (IS_ZEBRA_DEBUG_NHG
)
2893 zlog_debug("%s: id %u, already released", __func__
, id
);
2898 SET_FLAG(nhe
->flags
, NEXTHOP_GROUP_PROTO_RELEASED
);
2900 if (nhe
->refcnt
> 1) {
2901 if (IS_ZEBRA_DEBUG_NHG
)
2903 "%s: id %u, still being used by routes refcnt %u",
2904 __func__
, nhe
->id
, nhe
->refcnt
);
2908 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2909 zlog_debug("%s: deleted nhe %p (%u), vrf %d, type %s", __func__
,
2910 nhe
, nhe
->id
, nhe
->vrf_id
,
2911 zebra_route_string(nhe
->type
));
2916 struct nhg_score_proto_iter
{
2921 static void zebra_nhg_score_proto_entry(struct hash_bucket
*bucket
, void *arg
)
2923 struct nhg_hash_entry
*nhe
;
2924 struct nhg_score_proto_iter
*iter
;
2926 nhe
= (struct nhg_hash_entry
*)bucket
->data
;
2929 /* Needs to match type and outside zebra ID space */
2930 if (nhe
->type
== iter
->type
&& nhe
->id
>= ZEBRA_NHG_PROTO_LOWER
) {
2931 if (IS_ZEBRA_DEBUG_NHG_DETAIL
)
2933 "%s: found nhe %p (%u), vrf %d, type %s after client disconnect",
2934 __func__
, nhe
, nhe
->id
, nhe
->vrf_id
,
2935 zebra_route_string(nhe
->type
));
2937 /* Add to removal list */
2938 listnode_add(iter
->found
, nhe
);
2942 /* Remove specific by proto NHGs */
2943 unsigned long zebra_nhg_score_proto(int type
)
2945 struct nhg_hash_entry
*nhe
;
2946 struct nhg_score_proto_iter iter
= {};
2947 struct listnode
*ln
;
2948 unsigned long count
;
2951 iter
.found
= list_new();
2953 /* Find matching entries to remove */
2954 hash_iterate(zrouter
.nhgs_id
, zebra_nhg_score_proto_entry
, &iter
);
2956 /* Now remove them */
2957 for (ALL_LIST_ELEMENTS_RO(iter
.found
, ln
, nhe
)) {
2959 * This should be the last ref if we remove client routes too,
2960 * and thus should remove and free them.
2962 zebra_nhg_decrement_ref(nhe
);
2965 count
= iter
.found
->count
;
2966 list_delete(&iter
.found
);