back to topotato report
topotato coverage report
Current view: top level - zebra - zebra_nhg.c (source / functions) Hit Total Coverage
Test: test_pim6_bootstrap.py::PIM6Bootstrap Lines: 427 1486 28.7 %
Date: 2023-02-16 02:07:22 Functions: 53 103 51.5 %

          Line data    Source code
       1             : /* Zebra Nexthop Group Code.
       2             :  * Copyright (C) 2019 Cumulus Networks, Inc.
       3             :  *                    Donald Sharp
       4             :  *                    Stephen Worley
       5             :  *
       6             :  * This file is part of FRR.
       7             :  *
       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
      11             :  * later version.
      12             :  *
      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.
      17             :  *
      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
      21             :  * 02111-1307, USA.
      22             :  */
      23             : #include <zebra.h>
      24             : 
      25             : #include "lib/nexthop.h"
      26             : #include "lib/nexthop_group_private.h"
      27             : #include "lib/routemap.h"
      28             : #include "lib/mpls.h"
      29             : #include "lib/jhash.h"
      30             : #include "lib/debug.h"
      31             : #include "lib/lib_errors.h"
      32             : 
      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_srte.h"
      40             : #include "zebra/zserv.h"
      41             : #include "zebra/rt.h"
      42             : #include "zebra_errors.h"
      43             : #include "zebra_dplane.h"
      44             : #include "zebra/interface.h"
      45             : #include "zebra/zapi_msg.h"
      46             : #include "zebra/rib.h"
      47             : 
      48           3 : DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry");
      49           3 : DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected");
      50           3 : DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context");
      51             : 
      52             : /* Map backup nexthop indices between two nhes */
      53             : struct backup_nh_map_s {
      54             :         int map_count;
      55             : 
      56             :         struct {
      57             :                 uint8_t orig_idx;
      58             :                 uint8_t new_idx;
      59             :         } map[MULTIPATH_NUM];
      60             : };
      61             : 
      62             : /* id counter to keep in sync with kernel */
      63             : uint32_t id_counter;
      64             : 
      65             : /* Controlled through ui */
      66             : static bool g_nexthops_enabled = true;
      67             : static bool proto_nexthops_only;
      68             : static bool use_recursive_backups = true;
      69             : 
      70             : static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi,
      71             :                                            int type, bool from_dplane);
      72             : static void depends_add(struct nhg_connected_tree_head *head,
      73             :                         struct nhg_hash_entry *depend);
      74             : static struct nhg_hash_entry *
      75             : depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh,
      76             :                  afi_t afi, int type, bool from_dplane);
      77             : static struct nhg_hash_entry *
      78             : depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id);
      79             : static void depends_decrement_free(struct nhg_connected_tree_head *head);
      80             : 
      81             : static struct nhg_backup_info *
      82             : nhg_backup_copy(const struct nhg_backup_info *orig);
      83             : 
      84             : /* Helper function for getting the next allocatable ID */
      85          13 : static uint32_t nhg_get_next_id(void)
      86             : {
      87          13 :         while (1) {
      88          13 :                 id_counter++;
      89             : 
      90          13 :                 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
      91           0 :                         zlog_debug("%s: ID %u checking", __func__, id_counter);
      92             : 
      93          13 :                 if (id_counter == ZEBRA_NHG_PROTO_LOWER) {
      94           0 :                         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
      95           0 :                                 zlog_debug("%s: ID counter wrapped", __func__);
      96             : 
      97           0 :                         id_counter = 0;
      98           0 :                         continue;
      99             :                 }
     100             : 
     101          13 :                 if (zebra_nhg_lookup_id(id_counter)) {
     102           0 :                         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     103           0 :                                 zlog_debug("%s: ID already exists", __func__);
     104             : 
     105           0 :                         continue;
     106             :                 }
     107             : 
     108          13 :                 break;
     109             :         }
     110             : 
     111          13 :         return id_counter;
     112             : }
     113             : 
     114          13 : static void nhg_connected_free(struct nhg_connected *dep)
     115             : {
     116          13 :         XFREE(MTYPE_NHG_CONNECTED, dep);
     117           0 : }
     118             : 
     119          13 : static struct nhg_connected *nhg_connected_new(struct nhg_hash_entry *nhe)
     120             : {
     121          13 :         struct nhg_connected *new = NULL;
     122             : 
     123          26 :         new = XCALLOC(MTYPE_NHG_CONNECTED, sizeof(struct nhg_connected));
     124          13 :         new->nhe = nhe;
     125             : 
     126          13 :         return new;
     127             : }
     128             : 
     129          94 : void nhg_connected_tree_free(struct nhg_connected_tree_head *head)
     130             : {
     131          94 :         struct nhg_connected *rb_node_dep = NULL;
     132             : 
     133          94 :         if (!nhg_connected_tree_is_empty(head)) {
     134           0 :                 frr_each_safe(nhg_connected_tree, head, rb_node_dep) {
     135           0 :                         nhg_connected_tree_del(head, rb_node_dep);
     136           0 :                         nhg_connected_free(rb_node_dep);
     137             :                 }
     138             :         }
     139          94 : }
     140             : 
     141         210 : bool nhg_connected_tree_is_empty(const struct nhg_connected_tree_head *head)
     142             : {
     143          94 :         return nhg_connected_tree_count(head) ? false : true;
     144             : }
     145             : 
     146             : struct nhg_connected *
     147           0 : nhg_connected_tree_root(struct nhg_connected_tree_head *head)
     148             : {
     149           0 :         return nhg_connected_tree_first(head);
     150             : }
     151             : 
     152             : struct nhg_hash_entry *
     153          13 : nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head,
     154             :                            struct nhg_hash_entry *depend)
     155             : {
     156          13 :         struct nhg_connected lookup = {};
     157          13 :         struct nhg_connected *remove = NULL;
     158          13 :         struct nhg_hash_entry *removed_nhe;
     159             : 
     160          13 :         lookup.nhe = depend;
     161             : 
     162             :         /* Lookup to find the element, then remove it */
     163          13 :         remove = nhg_connected_tree_find(head, &lookup);
     164          13 :         if (remove)
     165             :                 /* Re-returning here just in case this API changes..
     166             :                  * the _del list api's are a bit undefined at the moment.
     167             :                  *
     168             :                  * So hopefully returning here will make it fail if the api
     169             :                  * changes to something different than currently expected.
     170             :                  */
     171          13 :                 remove = nhg_connected_tree_del(head, remove);
     172             : 
     173             :         /* If the entry was sucessfully removed, free the 'connected` struct */
     174          13 :         if (remove) {
     175          13 :                 removed_nhe = remove->nhe;
     176          13 :                 nhg_connected_free(remove);
     177          13 :                 return removed_nhe;
     178             :         }
     179             : 
     180             :         return NULL;
     181             : }
     182             : 
     183             : /* Assuming UNIQUE RB tree. If this changes, assumptions here about
     184             :  * insertion need to change.
     185             :  */
     186             : struct nhg_hash_entry *
     187          13 : nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
     188             :                            struct nhg_hash_entry *depend)
     189             : {
     190          13 :         struct nhg_connected *new = NULL;
     191             : 
     192          13 :         new = nhg_connected_new(depend);
     193             : 
     194             :         /* On success, NULL will be returned from the
     195             :          * RB code.
     196             :          */
     197          13 :         if (new && (nhg_connected_tree_add(head, new) == NULL))
     198             :                 return NULL;
     199             : 
     200             :         /* If it wasn't successful, it must be a duplicate. We enforce the
     201             :          * unique property for the `nhg_connected` tree.
     202             :          */
     203           0 :         nhg_connected_free(new);
     204             : 
     205           0 :         return depend;
     206             : }
     207             : 
     208             : static void
     209          45 : nhg_connected_tree_decrement_ref(struct nhg_connected_tree_head *head)
     210             : {
     211          45 :         struct nhg_connected *rb_node_dep = NULL;
     212             : 
     213          90 :         frr_each_safe(nhg_connected_tree, head, rb_node_dep) {
     214           0 :                 zebra_nhg_decrement_ref(rb_node_dep->nhe);
     215             :         }
     216          45 : }
     217             : 
     218             : static void
     219           0 : nhg_connected_tree_increment_ref(struct nhg_connected_tree_head *head)
     220             : {
     221           0 :         struct nhg_connected *rb_node_dep = NULL;
     222             : 
     223           0 :         frr_each(nhg_connected_tree, head, rb_node_dep) {
     224           0 :                 zebra_nhg_increment_ref(rb_node_dep->nhe);
     225             :         }
     226           0 : }
     227             : 
     228          23 : struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe)
     229             : {
     230          23 :         if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)
     231           0 :             && !zebra_nhg_depends_is_empty(nhe)) {
     232           0 :                 nhe = nhg_connected_tree_root(&nhe->nhg_depends)->nhe;
     233           0 :                 return zebra_nhg_resolve(nhe);
     234             :         }
     235             : 
     236             :         return nhe;
     237             : }
     238             : 
     239           0 : unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe)
     240             : {
     241           0 :         return nhg_connected_tree_count(&nhe->nhg_depends);
     242             : }
     243             : 
     244         116 : bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe)
     245             : {
     246           0 :         return nhg_connected_tree_is_empty(&nhe->nhg_depends);
     247             : }
     248             : 
     249           0 : static void zebra_nhg_depends_del(struct nhg_hash_entry *from,
     250             :                                   struct nhg_hash_entry *depend)
     251             : {
     252           0 :         nhg_connected_tree_del_nhe(&from->nhg_depends, depend);
     253             : }
     254             : 
     255          13 : static void zebra_nhg_depends_init(struct nhg_hash_entry *nhe)
     256             : {
     257          13 :         nhg_connected_tree_init(&nhe->nhg_depends);
     258             : }
     259             : 
     260           0 : unsigned int zebra_nhg_dependents_count(const struct nhg_hash_entry *nhe)
     261             : {
     262           0 :         return nhg_connected_tree_count(&nhe->nhg_dependents);
     263             : }
     264             : 
     265             : 
     266           0 : bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry *nhe)
     267             : {
     268           0 :         return nhg_connected_tree_is_empty(&nhe->nhg_dependents);
     269             : }
     270             : 
     271           0 : static void zebra_nhg_dependents_del(struct nhg_hash_entry *from,
     272             :                                      struct nhg_hash_entry *dependent)
     273             : {
     274           0 :         nhg_connected_tree_del_nhe(&from->nhg_dependents, dependent);
     275             : }
     276             : 
     277           0 : static void zebra_nhg_dependents_add(struct nhg_hash_entry *to,
     278             :                                      struct nhg_hash_entry *dependent)
     279             : {
     280           0 :         nhg_connected_tree_add_nhe(&to->nhg_dependents, dependent);
     281             : }
     282             : 
     283          13 : static void zebra_nhg_dependents_init(struct nhg_hash_entry *nhe)
     284             : {
     285          13 :         nhg_connected_tree_init(&nhe->nhg_dependents);
     286             : }
     287             : 
     288             : /* Release this nhe from anything depending on it */
     289          13 : static void zebra_nhg_dependents_release(struct nhg_hash_entry *nhe)
     290             : {
     291          13 :         struct nhg_connected *rb_node_dep = NULL;
     292             : 
     293          26 :         frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) {
     294           0 :                 zebra_nhg_depends_del(rb_node_dep->nhe, nhe);
     295             :                 /* recheck validity of the dependent */
     296           0 :                 zebra_nhg_check_valid(rb_node_dep->nhe);
     297             :         }
     298          13 : }
     299             : 
     300             : /* Release this nhe from anything that it depends on */
     301          13 : static void zebra_nhg_depends_release(struct nhg_hash_entry *nhe)
     302             : {
     303          13 :         if (!zebra_nhg_depends_is_empty(nhe)) {
     304           0 :                 struct nhg_connected *rb_node_dep = NULL;
     305             : 
     306           0 :                 frr_each_safe(nhg_connected_tree, &nhe->nhg_depends,
     307             :                                rb_node_dep) {
     308           0 :                         zebra_nhg_dependents_del(rb_node_dep->nhe, nhe);
     309             :                 }
     310             :         }
     311          13 : }
     312             : 
     313             : 
     314          19 : struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id)
     315             : {
     316          19 :         struct nhg_hash_entry lookup = {};
     317             : 
     318          19 :         lookup.id = id;
     319          19 :         return hash_lookup(zrouter.nhgs_id, &lookup);
     320             : }
     321             : 
     322          13 : static int zebra_nhg_insert_id(struct nhg_hash_entry *nhe)
     323             : {
     324          13 :         if (hash_lookup(zrouter.nhgs_id, nhe)) {
     325           0 :                 flog_err(
     326             :                         EC_ZEBRA_NHG_TABLE_INSERT_FAILED,
     327             :                         "Failed inserting NHG %pNG into the ID hash table, entry already exists",
     328             :                         nhe);
     329           0 :                 return -1;
     330             :         }
     331             : 
     332          13 :         (void)hash_get(zrouter.nhgs_id, nhe, hash_alloc_intern);
     333             : 
     334          13 :         return 0;
     335             : }
     336             : 
     337          13 : static void zebra_nhg_set_if(struct nhg_hash_entry *nhe, struct interface *ifp)
     338             : {
     339          13 :         nhe->ifp = ifp;
     340          13 :         if_nhg_dependents_add(ifp, nhe);
     341          13 : }
     342             : 
     343             : static void
     344          13 : zebra_nhg_connect_depends(struct nhg_hash_entry *nhe,
     345             :                           struct nhg_connected_tree_head *nhg_depends)
     346             : {
     347          13 :         struct nhg_connected *rb_node_dep = NULL;
     348             : 
     349             :         /* This has been allocated higher above in the stack. Could probably
     350             :          * re-allocate and free the old stuff but just using the same memory
     351             :          * for now. Otherwise, their might be a time trade-off for repeated
     352             :          * alloc/frees as startup.
     353             :          */
     354          13 :         nhe->nhg_depends = *nhg_depends;
     355             : 
     356             :         /* Attach backpointer to anything that it depends on */
     357          13 :         zebra_nhg_dependents_init(nhe);
     358          13 :         if (!zebra_nhg_depends_is_empty(nhe)) {
     359           0 :                 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
     360           0 :                         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     361           0 :                                 zlog_debug("%s: nhe %p (%pNG), dep %p (%pNG)",
     362             :                                            __func__, nhe, nhe, rb_node_dep->nhe,
     363             :                                            rb_node_dep->nhe);
     364             : 
     365           0 :                         zebra_nhg_dependents_add(rb_node_dep->nhe, nhe);
     366             :                 }
     367             :         }
     368          13 : }
     369             : 
     370             : /* Init an nhe, for use in a hash lookup for example */
     371          16 : void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi,
     372             :                     const struct nexthop *nh)
     373             : {
     374          16 :         memset(nhe, 0, sizeof(struct nhg_hash_entry));
     375          16 :         nhe->vrf_id = VRF_DEFAULT;
     376          16 :         nhe->type = ZEBRA_ROUTE_NHG;
     377          16 :         nhe->afi = AFI_UNSPEC;
     378             : 
     379             :         /* There are some special rules that apply to groups representing
     380             :          * a single nexthop.
     381             :          */
     382          16 :         if (nh && (nh->next == NULL)) {
     383          16 :                 switch (nh->type) {
     384          16 :                 case NEXTHOP_TYPE_IFINDEX:
     385             :                 case NEXTHOP_TYPE_BLACKHOLE:
     386             :                         /*
     387             :                          * This switch case handles setting the afi different
     388             :                          * for ipv4/v6 routes. Ifindex/blackhole nexthop
     389             :                          * objects cannot be ambiguous, they must be Address
     390             :                          * Family specific. If we get here, we will either use
     391             :                          * the AF of the route, or the one we got passed from
     392             :                          * here from the kernel.
     393             :                          */
     394          16 :                         nhe->afi = afi;
     395          16 :                         break;
     396           0 :                 case NEXTHOP_TYPE_IPV4_IFINDEX:
     397             :                 case NEXTHOP_TYPE_IPV4:
     398           0 :                         nhe->afi = AFI_IP;
     399           0 :                         break;
     400           0 :                 case NEXTHOP_TYPE_IPV6_IFINDEX:
     401             :                 case NEXTHOP_TYPE_IPV6:
     402           0 :                         nhe->afi = AFI_IP6;
     403           0 :                         break;
     404             :                 }
     405             :         }
     406          16 : }
     407             : 
     408          45 : struct nhg_hash_entry *zebra_nhg_alloc(void)
     409             : {
     410          45 :         struct nhg_hash_entry *nhe;
     411             : 
     412           0 :         nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
     413             : 
     414          45 :         return nhe;
     415             : }
     416             : 
     417             : /*
     418             :  * Allocate new nhe and make shallow copy of 'orig'; no
     419             :  * recursive info is copied.
     420             :  */
     421          45 : struct nhg_hash_entry *zebra_nhe_copy(const struct nhg_hash_entry *orig,
     422             :                                       uint32_t id)
     423             : {
     424          45 :         struct nhg_hash_entry *nhe;
     425             : 
     426          45 :         nhe = zebra_nhg_alloc();
     427             : 
     428          45 :         nhe->id = id;
     429             : 
     430          45 :         nexthop_group_copy(&(nhe->nhg), &(orig->nhg));
     431             : 
     432          45 :         nhe->vrf_id = orig->vrf_id;
     433          45 :         nhe->afi = orig->afi;
     434          45 :         nhe->type = orig->type ? orig->type : ZEBRA_ROUTE_NHG;
     435          45 :         nhe->refcnt = 0;
     436          45 :         nhe->dplane_ref = zebra_router_get_next_sequence();
     437             : 
     438             :         /* Copy backup info also, if present */
     439          45 :         if (orig->backup_info)
     440           0 :                 nhe->backup_info = nhg_backup_copy(orig->backup_info);
     441             : 
     442          45 :         return nhe;
     443             : }
     444             : 
     445             : /* Allocation via hash handler */
     446          13 : static void *zebra_nhg_hash_alloc(void *arg)
     447             : {
     448          13 :         struct nhg_hash_entry *nhe = NULL;
     449          13 :         struct nhg_hash_entry *copy = arg;
     450             : 
     451          13 :         nhe = zebra_nhe_copy(copy, copy->id);
     452             : 
     453             :         /* Mark duplicate nexthops in a group at creation time. */
     454          13 :         nexthop_group_mark_duplicates(&(nhe->nhg));
     455             : 
     456             :         /*
     457             :          * Add the ifp now if it's not a group or recursive and has ifindex.
     458             :          *
     459             :          * A proto-owned ID is always a group.
     460             :          */
     461          13 :         if (!PROTO_OWNED(nhe) && nhe->nhg.nexthop && !nhe->nhg.nexthop->next
     462          13 :             && !nhe->nhg.nexthop->resolved && nhe->nhg.nexthop->ifindex) {
     463          13 :                 struct interface *ifp = NULL;
     464             : 
     465          13 :                 ifp = if_lookup_by_index(nhe->nhg.nexthop->ifindex,
     466             :                                          nhe->nhg.nexthop->vrf_id);
     467          13 :                 if (ifp)
     468          13 :                         zebra_nhg_set_if(nhe, ifp);
     469             :                 else {
     470           0 :                         if (IS_ZEBRA_DEBUG_NHG)
     471           0 :                                 zlog_debug(
     472             :                                         "Failed to lookup an interface with ifindex=%d in vrf=%u for NHE %pNG",
     473             :                                         nhe->nhg.nexthop->ifindex,
     474             :                                         nhe->nhg.nexthop->vrf_id, nhe);
     475             :                 }
     476             :         }
     477             : 
     478          13 :         return nhe;
     479             : }
     480             : 
     481          57 : uint32_t zebra_nhg_hash_key(const void *arg)
     482             : {
     483          57 :         const struct nhg_hash_entry *nhe = arg;
     484          57 :         uint32_t key = 0x5a351234;
     485          57 :         uint32_t primary = 0;
     486          57 :         uint32_t backup = 0;
     487             : 
     488          57 :         primary = nexthop_group_hash(&(nhe->nhg));
     489          57 :         if (nhe->backup_info)
     490           0 :                 backup = nexthop_group_hash(&(nhe->backup_info->nhe->nhg));
     491             : 
     492          57 :         key = jhash_3words(primary, backup, nhe->type, key);
     493             : 
     494          57 :         key = jhash_2words(nhe->vrf_id, nhe->afi, key);
     495             : 
     496          57 :         return key;
     497             : }
     498             : 
     499          56 : uint32_t zebra_nhg_id_key(const void *arg)
     500             : {
     501          56 :         const struct nhg_hash_entry *nhe = arg;
     502             : 
     503          56 :         return nhe->id;
     504             : }
     505             : 
     506             : /* Helper with common nhg/nhe nexthop comparison logic */
     507          41 : static bool nhg_compare_nexthops(const struct nexthop *nh1,
     508             :                                  const struct nexthop *nh2)
     509             : {
     510          41 :         assert(nh1 != NULL && nh2 != NULL);
     511             : 
     512             :         /*
     513             :          * We have to check the active flag of each individual one,
     514             :          * not just the overall active_num. This solves the special case
     515             :          * issue of a route with a nexthop group with one nexthop
     516             :          * resolving to itself and thus marking it inactive. If we
     517             :          * have two different routes each wanting to mark a different
     518             :          * nexthop inactive, they need to hash to two different groups.
     519             :          *
     520             :          * If we just hashed on num_active, they would hash the same
     521             :          * which is incorrect.
     522             :          *
     523             :          * ex)
     524             :          *      1.1.1.0/24
     525             :          *           -> 1.1.1.1 dummy1 (inactive)
     526             :          *           -> 1.1.2.1 dummy2
     527             :          *
     528             :          *      1.1.2.0/24
     529             :          *           -> 1.1.1.1 dummy1
     530             :          *           -> 1.1.2.1 dummy2 (inactive)
     531             :          *
     532             :          * Without checking each individual one, they would hash to
     533             :          * the same group and both have 1.1.1.1 dummy1 marked inactive.
     534             :          *
     535             :          */
     536          41 :         if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_ACTIVE)
     537          41 :             != CHECK_FLAG(nh2->flags, NEXTHOP_FLAG_ACTIVE))
     538             :                 return false;
     539             : 
     540          19 :         if (!nexthop_same(nh1, nh2))
     541             :                 return false;
     542             : 
     543             :         return true;
     544             : }
     545             : 
     546          54 : bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
     547             : {
     548          54 :         const struct nhg_hash_entry *nhe1 = arg1;
     549          54 :         const struct nhg_hash_entry *nhe2 = arg2;
     550          54 :         struct nexthop *nexthop1;
     551          54 :         struct nexthop *nexthop2;
     552             : 
     553             :         /* No matter what if they equal IDs, assume equal */
     554          54 :         if (nhe1->id && nhe2->id && (nhe1->id == nhe2->id))
     555             :                 return true;
     556             : 
     557          41 :         if (nhe1->type != nhe2->type)
     558             :                 return false;
     559             : 
     560          41 :         if (nhe1->vrf_id != nhe2->vrf_id)
     561             :                 return false;
     562             : 
     563          41 :         if (nhe1->afi != nhe2->afi)
     564             :                 return false;
     565             : 
     566          41 :         if (nhe1->nhg.nhgr.buckets != nhe2->nhg.nhgr.buckets)
     567             :                 return false;
     568             : 
     569          41 :         if (nhe1->nhg.nhgr.idle_timer != nhe2->nhg.nhgr.idle_timer)
     570             :                 return false;
     571             : 
     572          41 :         if (nhe1->nhg.nhgr.unbalanced_timer != nhe2->nhg.nhgr.unbalanced_timer)
     573             :                 return false;
     574             : 
     575             :         /* Nexthops should be in-order, so we simply compare them in-place */
     576          41 :         for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop;
     577          60 :              nexthop1 && nexthop2;
     578          19 :              nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
     579             : 
     580          41 :                 if (!nhg_compare_nexthops(nexthop1, nexthop2))
     581             :                         return false;
     582             :         }
     583             : 
     584             :         /* Check for unequal list lengths */
     585          19 :         if (nexthop1 || nexthop2)
     586             :                 return false;
     587             : 
     588             :         /* If there's no backup info, comparison is done. */
     589          19 :         if ((nhe1->backup_info == NULL) && (nhe2->backup_info == NULL))
     590             :                 return true;
     591             : 
     592             :         /* Compare backup info also - test the easy things first */
     593           0 :         if (nhe1->backup_info && (nhe2->backup_info == NULL))
     594             :                 return false;
     595           0 :         if (nhe2->backup_info && (nhe1->backup_info == NULL))
     596             :                 return false;
     597             : 
     598             :         /* Compare number of backups before actually comparing any */
     599           0 :         for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop,
     600           0 :              nexthop2 = nhe2->backup_info->nhe->nhg.nexthop;
     601           0 :              nexthop1 && nexthop2;
     602           0 :              nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
     603           0 :                 ;
     604             :         }
     605             : 
     606             :         /* Did we find the end of one list before the other? */
     607           0 :         if (nexthop1 || nexthop2)
     608             :                 return false;
     609             : 
     610             :         /* Have to compare the backup nexthops */
     611             :         for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop,
     612             :              nexthop2 = nhe2->backup_info->nhe->nhg.nexthop;
     613           0 :              nexthop1 && nexthop2;
     614           0 :              nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
     615             : 
     616           0 :                 if (!nhg_compare_nexthops(nexthop1, nexthop2))
     617             :                         return false;
     618             :         }
     619             : 
     620             :         return true;
     621             : }
     622             : 
     623          19 : bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2)
     624             : {
     625          19 :         const struct nhg_hash_entry *nhe1 = arg1;
     626          19 :         const struct nhg_hash_entry *nhe2 = arg2;
     627             : 
     628          19 :         return nhe1->id == nhe2->id;
     629             : }
     630             : 
     631           0 : static int zebra_nhg_process_grp(struct nexthop_group *nhg,
     632             :                                  struct nhg_connected_tree_head *depends,
     633             :                                  struct nh_grp *grp, uint8_t count,
     634             :                                  struct nhg_resilience *resilience)
     635             : {
     636           0 :         nhg_connected_tree_init(depends);
     637             : 
     638           0 :         for (int i = 0; i < count; i++) {
     639           0 :                 struct nhg_hash_entry *depend = NULL;
     640             :                 /* We do not care about nexthop_grp.weight at
     641             :                  * this time. But we should figure out
     642             :                  * how to adapt this to our code in
     643             :                  * the future.
     644             :                  */
     645           0 :                 depend = depends_find_id_add(depends, grp[i].id);
     646             : 
     647           0 :                 if (!depend) {
     648           0 :                         flog_err(
     649             :                                 EC_ZEBRA_NHG_SYNC,
     650             :                                 "Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table",
     651             :                                 grp[i].id);
     652           0 :                         return -1;
     653             :                 }
     654             : 
     655             :                 /*
     656             :                  * If this is a nexthop with its own group
     657             :                  * dependencies, add them as well. Not sure its
     658             :                  * even possible to have a group within a group
     659             :                  * in the kernel.
     660             :                  */
     661             : 
     662           0 :                 copy_nexthops(&nhg->nexthop, depend->nhg.nexthop, NULL);
     663             :         }
     664             : 
     665           0 :         if (resilience)
     666           0 :                 nhg->nhgr = *resilience;
     667             : 
     668             :         return 0;
     669             : }
     670             : 
     671           0 : static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends,
     672             :                                     struct nexthop *nh, afi_t afi, int type)
     673             : {
     674           0 :         struct nhg_hash_entry *depend = NULL;
     675           0 :         struct nexthop_group resolved_ng = {};
     676             : 
     677           0 :         resolved_ng.nexthop = nh;
     678             : 
     679           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     680           0 :                 zlog_debug("%s: head %p, nh %pNHv",
     681             :                            __func__, nhg_depends, nh);
     682             : 
     683           0 :         depend = zebra_nhg_rib_find(0, &resolved_ng, afi, type);
     684             : 
     685           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     686           0 :                 zlog_debug("%s: nh %pNHv => %p (%u)",
     687             :                            __func__, nh, depend,
     688             :                            depend ? depend->id : 0);
     689             : 
     690           0 :         if (depend)
     691           0 :                 depends_add(nhg_depends, depend);
     692           0 : }
     693             : 
     694             : /*
     695             :  * Lookup an nhe in the global hash, using data from another nhe. If 'lookup'
     696             :  * has an id value, that's used. Create a new global/shared nhe if not found.
     697             :  */
     698          32 : static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */
     699             :                            struct nhg_hash_entry *lookup,
     700             :                            struct nhg_connected_tree_head *nhg_depends,
     701             :                            afi_t afi, bool from_dplane)
     702             : {
     703          32 :         bool created = false;
     704          32 :         bool recursive = false;
     705          32 :         struct nhg_hash_entry *newnhe, *backup_nhe;
     706          32 :         struct nexthop *nh = NULL;
     707             : 
     708          32 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     709           0 :                 zlog_debug(
     710             :                         "%s: id %u, lookup %p, vrf %d, type %d, depends %p%s",
     711             :                         __func__, lookup->id, lookup, lookup->vrf_id,
     712             :                         lookup->type, nhg_depends,
     713             :                         (from_dplane ? " (from dplane)" : ""));
     714             : 
     715          32 :         if (lookup->id)
     716           0 :                 (*nhe) = zebra_nhg_lookup_id(lookup->id);
     717             :         else
     718          32 :                 (*nhe) = hash_lookup(zrouter.nhgs, lookup);
     719             : 
     720          32 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     721           0 :                 zlog_debug("%s: lookup => %p (%pNG)", __func__, *nhe, *nhe);
     722             : 
     723             :         /* If we found an existing object, we're done */
     724          32 :         if (*nhe)
     725          19 :                 goto done;
     726             : 
     727             :         /* We're going to create/insert a new nhe:
     728             :          * assign the next global id value if necessary.
     729             :          */
     730          13 :         if (lookup->id == 0)
     731          13 :                 lookup->id = nhg_get_next_id();
     732             : 
     733          13 :         if (!from_dplane && lookup->id < ZEBRA_NHG_PROTO_LOWER) {
     734             :                 /*
     735             :                  * This is a zebra hashed/owned NHG.
     736             :                  *
     737             :                  * It goes in HASH and ID table.
     738             :                  */
     739          13 :                 newnhe = hash_get(zrouter.nhgs, lookup, zebra_nhg_hash_alloc);
     740          13 :                 zebra_nhg_insert_id(newnhe);
     741             :         } else {
     742             :                 /*
     743             :                  * This is upperproto owned NHG or one we read in from dataplane
     744             :                  * and should not be hashed to.
     745             :                  *
     746             :                  * It goes in ID table.
     747             :                  */
     748           0 :                 newnhe =
     749           0 :                         hash_get(zrouter.nhgs_id, lookup, zebra_nhg_hash_alloc);
     750             :         }
     751             : 
     752          13 :         created = true;
     753             : 
     754             :         /* Mail back the new object */
     755          13 :         *nhe = newnhe;
     756             : 
     757          13 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     758           0 :                 zlog_debug("%s: => created %p (%pNG)", __func__, newnhe,
     759             :                            newnhe);
     760             : 
     761             :         /* Only hash/lookup the depends if the first lookup
     762             :          * fails to find something. This should hopefully save a
     763             :          * lot of cycles for larger ecmp sizes.
     764             :          */
     765          13 :         if (nhg_depends) {
     766             :                 /* If you don't want to hash on each nexthop in the
     767             :                  * nexthop group struct you can pass the depends
     768             :                  * directly. Kernel-side we do this since it just looks
     769             :                  * them up via IDs.
     770             :                  */
     771           0 :                 zebra_nhg_connect_depends(newnhe, nhg_depends);
     772           0 :                 goto done;
     773             :         }
     774             : 
     775             :         /* Prepare dependency relationships if this is not a
     776             :          * singleton nexthop. There are two cases: a single
     777             :          * recursive nexthop, where we need a relationship to the
     778             :          * resolving nexthop; or a group of nexthops, where we need
     779             :          * relationships with the corresponding singletons.
     780             :          */
     781          13 :         zebra_nhg_depends_init(newnhe);
     782             : 
     783          13 :         nh = newnhe->nhg.nexthop;
     784             : 
     785          13 :         if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE))
     786           6 :                 SET_FLAG(newnhe->flags, NEXTHOP_GROUP_VALID);
     787             : 
     788          13 :         if (nh->next == NULL && newnhe->id < ZEBRA_NHG_PROTO_LOWER) {
     789          13 :                 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) {
     790             :                         /* Single recursive nexthop */
     791           0 :                         handle_recursive_depend(&newnhe->nhg_depends,
     792             :                                                 nh->resolved, afi,
     793             :                                                 newnhe->type);
     794           0 :                         recursive = true;
     795             :                 }
     796             :         } else {
     797             :                 /* Proto-owned are groups by default */
     798             :                 /* List of nexthops */
     799           0 :                 for (nh = newnhe->nhg.nexthop; nh; nh = nh->next) {
     800           0 :                         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     801           0 :                                 zlog_debug("%s: depends NH %pNHv %s",
     802             :                                            __func__, nh,
     803             :                                            CHECK_FLAG(nh->flags,
     804             :                                                       NEXTHOP_FLAG_RECURSIVE) ?
     805             :                                            "(R)" : "");
     806             : 
     807           0 :                         depends_find_add(&newnhe->nhg_depends, nh, afi,
     808             :                                          newnhe->type, from_dplane);
     809             :                 }
     810             :         }
     811             : 
     812           0 :         if (recursive)
     813           0 :                 SET_FLAG(newnhe->flags, NEXTHOP_GROUP_RECURSIVE);
     814             : 
     815             :         /* Attach dependent backpointers to singletons */
     816          13 :         zebra_nhg_connect_depends(newnhe, &newnhe->nhg_depends);
     817             : 
     818             :         /**
     819             :          * Backup Nexthops
     820             :          */
     821             : 
     822          26 :         if (zebra_nhg_get_backup_nhg(newnhe) == NULL ||
     823           0 :             zebra_nhg_get_backup_nhg(newnhe)->nexthop == NULL)
     824          13 :                 goto done;
     825             : 
     826             :         /* If there are backup nexthops, add them to the backup
     827             :          * depends tree. The rules here are a little different.
     828             :          */
     829           0 :         recursive = false;
     830           0 :         backup_nhe = newnhe->backup_info->nhe;
     831             : 
     832           0 :         nh = backup_nhe->nhg.nexthop;
     833             : 
     834             :         /* Singleton recursive NH */
     835           0 :         if (nh->next == NULL &&
     836           0 :             CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) {
     837           0 :                 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     838           0 :                         zlog_debug("%s: backup depend NH %pNHv (R)",
     839             :                                    __func__, nh);
     840             : 
     841             :                 /* Single recursive nexthop */
     842           0 :                 handle_recursive_depend(&backup_nhe->nhg_depends, nh->resolved,
     843             :                                         afi, backup_nhe->type);
     844           0 :                 recursive = true;
     845             :         } else {
     846             :                 /* One or more backup NHs */
     847           0 :                 for (; nh; nh = nh->next) {
     848           0 :                         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     849           0 :                                 zlog_debug("%s: backup depend NH %pNHv %s",
     850             :                                            __func__, nh,
     851             :                                            CHECK_FLAG(nh->flags,
     852             :                                                       NEXTHOP_FLAG_RECURSIVE) ?
     853             :                                            "(R)" : "");
     854             : 
     855           0 :                         depends_find_add(&backup_nhe->nhg_depends, nh, afi,
     856             :                                          backup_nhe->type, from_dplane);
     857             :                 }
     858             :         }
     859             : 
     860           0 :         if (recursive)
     861           0 :                 SET_FLAG(backup_nhe->flags, NEXTHOP_GROUP_RECURSIVE);
     862             : 
     863          32 : done:
     864             :         /* Reset time since last update */
     865          32 :         (*nhe)->uptime = monotime(NULL);
     866             : 
     867          32 :         return created;
     868             : }
     869             : 
     870             : /*
     871             :  * Lookup or create an nhe, based on an nhg or an nhe id.
     872             :  */
     873           0 : static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id,
     874             :                            struct nexthop_group *nhg,
     875             :                            struct nhg_connected_tree_head *nhg_depends,
     876             :                            vrf_id_t vrf_id, afi_t afi, int type,
     877             :                            bool from_dplane)
     878             : {
     879           0 :         struct nhg_hash_entry lookup = {};
     880           0 :         bool created = false;
     881             : 
     882           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     883           0 :                 zlog_debug("%s: id %u, nhg %p, vrf %d, type %d, depends %p",
     884             :                            __func__, id, nhg, vrf_id, type,
     885             :                            nhg_depends);
     886             : 
     887             :         /* Use a temporary nhe and call into the superset/common code */
     888           0 :         lookup.id = id;
     889           0 :         lookup.type = type ? type : ZEBRA_ROUTE_NHG;
     890           0 :         lookup.nhg = *nhg;
     891             : 
     892           0 :         lookup.vrf_id = vrf_id;
     893           0 :         if (nhg_depends || lookup.nhg.nexthop->next) {
     894             :                 /* Groups can have all vrfs and AF's in them */
     895           0 :                 lookup.afi = AFI_UNSPEC;
     896             :         } else {
     897           0 :                 switch (lookup.nhg.nexthop->type) {
     898           0 :                 case (NEXTHOP_TYPE_IFINDEX):
     899             :                 case (NEXTHOP_TYPE_BLACKHOLE):
     900             :                         /*
     901             :                          * This switch case handles setting the afi different
     902             :                          * for ipv4/v6 routes. Ifindex/blackhole nexthop
     903             :                          * objects cannot be ambiguous, they must be Address
     904             :                          * Family specific. If we get here, we will either use
     905             :                          * the AF of the route, or the one we got passed from
     906             :                          * here from the kernel.
     907             :                          */
     908           0 :                         lookup.afi = afi;
     909           0 :                         break;
     910           0 :                 case (NEXTHOP_TYPE_IPV4_IFINDEX):
     911             :                 case (NEXTHOP_TYPE_IPV4):
     912           0 :                         lookup.afi = AFI_IP;
     913           0 :                         break;
     914           0 :                 case (NEXTHOP_TYPE_IPV6_IFINDEX):
     915             :                 case (NEXTHOP_TYPE_IPV6):
     916           0 :                         lookup.afi = AFI_IP6;
     917           0 :                         break;
     918             :                 }
     919             :         }
     920             : 
     921           0 :         created = zebra_nhe_find(nhe, &lookup, nhg_depends, afi, from_dplane);
     922             : 
     923           0 :         return created;
     924             : }
     925             : 
     926             : /* Find/create a single nexthop */
     927           0 : static struct nhg_hash_entry *zebra_nhg_find_nexthop(uint32_t id,
     928             :                                                      struct nexthop *nh,
     929             :                                                      afi_t afi, int type,
     930             :                                                      bool from_dplane)
     931             : {
     932           0 :         struct nhg_hash_entry *nhe = NULL;
     933           0 :         struct nexthop_group nhg = {};
     934           0 :         vrf_id_t vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nh->vrf_id;
     935             : 
     936           0 :         nexthop_group_add_sorted(&nhg, nh);
     937             : 
     938           0 :         zebra_nhg_find(&nhe, id, &nhg, NULL, vrf_id, afi, type, from_dplane);
     939             : 
     940           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
     941           0 :                 zlog_debug("%s: nh %pNHv => %p (%pNG)", __func__, nh, nhe, nhe);
     942             : 
     943           0 :         return nhe;
     944             : }
     945             : 
     946           0 : static uint32_t nhg_ctx_get_id(const struct nhg_ctx *ctx)
     947             : {
     948           0 :         return ctx->id;
     949             : }
     950             : 
     951           0 : static void nhg_ctx_set_status(struct nhg_ctx *ctx, enum nhg_ctx_status status)
     952             : {
     953           0 :         ctx->status = status;
     954             : }
     955             : 
     956           0 : static enum nhg_ctx_status nhg_ctx_get_status(const struct nhg_ctx *ctx)
     957             : {
     958           0 :         return ctx->status;
     959             : }
     960             : 
     961           0 : static void nhg_ctx_set_op(struct nhg_ctx *ctx, enum nhg_ctx_op_e op)
     962             : {
     963           0 :         ctx->op = op;
     964             : }
     965             : 
     966           0 : static enum nhg_ctx_op_e nhg_ctx_get_op(const struct nhg_ctx *ctx)
     967             : {
     968           0 :         return ctx->op;
     969             : }
     970             : 
     971           0 : static vrf_id_t nhg_ctx_get_vrf_id(const struct nhg_ctx *ctx)
     972             : {
     973           0 :         return ctx->vrf_id;
     974             : }
     975             : 
     976           0 : static int nhg_ctx_get_type(const struct nhg_ctx *ctx)
     977             : {
     978           0 :         return ctx->type;
     979             : }
     980             : 
     981           0 : static int nhg_ctx_get_afi(const struct nhg_ctx *ctx)
     982             : {
     983           0 :         return ctx->afi;
     984             : }
     985             : 
     986           0 : static struct nexthop *nhg_ctx_get_nh(struct nhg_ctx *ctx)
     987             : {
     988           0 :         return &ctx->u.nh;
     989             : }
     990             : 
     991           0 : static uint8_t nhg_ctx_get_count(const struct nhg_ctx *ctx)
     992             : {
     993           0 :         return ctx->count;
     994             : }
     995             : 
     996           0 : static struct nh_grp *nhg_ctx_get_grp(struct nhg_ctx *ctx)
     997             : {
     998           0 :         return ctx->u.grp;
     999             : }
    1000             : 
    1001           0 : static struct nhg_resilience *nhg_ctx_get_resilience(struct nhg_ctx *ctx)
    1002             : {
    1003           0 :         return &ctx->resilience;
    1004             : }
    1005             : 
    1006           0 : static struct nhg_ctx *nhg_ctx_new(void)
    1007             : {
    1008           0 :         struct nhg_ctx *new;
    1009             : 
    1010           0 :         new = XCALLOC(MTYPE_NHG_CTX, sizeof(struct nhg_ctx));
    1011             : 
    1012           0 :         return new;
    1013             : }
    1014             : 
    1015           0 : void nhg_ctx_free(struct nhg_ctx **ctx)
    1016             : {
    1017           0 :         struct nexthop *nh;
    1018             : 
    1019           0 :         if (ctx == NULL)
    1020             :                 return;
    1021             : 
    1022           0 :         assert((*ctx) != NULL);
    1023             : 
    1024           0 :         if (nhg_ctx_get_count(*ctx))
    1025           0 :                 goto done;
    1026             : 
    1027           0 :         nh = nhg_ctx_get_nh(*ctx);
    1028             : 
    1029           0 :         nexthop_del_labels(nh);
    1030           0 :         nexthop_del_srv6_seg6local(nh);
    1031           0 :         nexthop_del_srv6_seg6(nh);
    1032             : 
    1033           0 : done:
    1034           0 :         XFREE(MTYPE_NHG_CTX, *ctx);
    1035             : }
    1036             : 
    1037           0 : static struct nhg_ctx *nhg_ctx_init(uint32_t id, struct nexthop *nh,
    1038             :                                     struct nh_grp *grp, vrf_id_t vrf_id,
    1039             :                                     afi_t afi, int type, uint8_t count,
    1040             :                                     struct nhg_resilience *resilience)
    1041             : {
    1042           0 :         struct nhg_ctx *ctx = NULL;
    1043             : 
    1044           0 :         ctx = nhg_ctx_new();
    1045             : 
    1046           0 :         ctx->id = id;
    1047           0 :         ctx->vrf_id = vrf_id;
    1048           0 :         ctx->afi = afi;
    1049           0 :         ctx->type = type;
    1050           0 :         ctx->count = count;
    1051             : 
    1052           0 :         if (resilience)
    1053           0 :                 ctx->resilience = *resilience;
    1054             : 
    1055           0 :         if (count)
    1056             :                 /* Copy over the array */
    1057           0 :                 memcpy(&ctx->u.grp, grp, count * sizeof(struct nh_grp));
    1058           0 :         else if (nh)
    1059           0 :                 ctx->u.nh = *nh;
    1060             : 
    1061           0 :         return ctx;
    1062             : }
    1063             : 
    1064           0 : static void zebra_nhg_set_valid(struct nhg_hash_entry *nhe)
    1065             : {
    1066           0 :         struct nhg_connected *rb_node_dep;
    1067             : 
    1068           0 :         SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
    1069             : 
    1070           0 :         frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
    1071           0 :                 zebra_nhg_set_valid(rb_node_dep->nhe);
    1072           0 : }
    1073             : 
    1074           0 : static void zebra_nhg_set_invalid(struct nhg_hash_entry *nhe)
    1075             : {
    1076           0 :         struct nhg_connected *rb_node_dep;
    1077             : 
    1078           0 :         UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
    1079             : 
    1080             :         /* If we're in shutdown, this interface event needs to clean
    1081             :          * up installed NHGs, so don't clear that flag directly.
    1082             :          */
    1083           0 :         if (!zebra_router_in_shutdown())
    1084           0 :                 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
    1085             : 
    1086             :         /* Update validity of nexthops depending on it */
    1087           0 :         frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
    1088           0 :                 zebra_nhg_check_valid(rb_node_dep->nhe);
    1089           0 : }
    1090             : 
    1091           0 : void zebra_nhg_check_valid(struct nhg_hash_entry *nhe)
    1092             : {
    1093           0 :         struct nhg_connected *rb_node_dep = NULL;
    1094           0 :         bool valid = false;
    1095             : 
    1096             :         /* If anthing else in the group is valid, the group is valid */
    1097           0 :         frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
    1098           0 :                 if (CHECK_FLAG(rb_node_dep->nhe->flags, NEXTHOP_GROUP_VALID)) {
    1099           0 :                         valid = true;
    1100           0 :                         goto done;
    1101             :                 }
    1102             :         }
    1103             : 
    1104           0 : done:
    1105           0 :         if (valid)
    1106           0 :                 zebra_nhg_set_valid(nhe);
    1107             :         else
    1108           0 :                 zebra_nhg_set_invalid(nhe);
    1109           0 : }
    1110             : 
    1111          13 : static void zebra_nhg_release_all_deps(struct nhg_hash_entry *nhe)
    1112             : {
    1113             :         /* Remove it from any lists it may be on */
    1114          13 :         zebra_nhg_depends_release(nhe);
    1115          13 :         zebra_nhg_dependents_release(nhe);
    1116          13 :         if (nhe->ifp)
    1117          13 :                 if_nhg_dependents_del(nhe->ifp, nhe);
    1118          13 : }
    1119             : 
    1120          13 : static void zebra_nhg_release(struct nhg_hash_entry *nhe)
    1121             : {
    1122          13 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1123           0 :                 zlog_debug("%s: nhe %p (%pNG)", __func__, nhe, nhe);
    1124             : 
    1125          13 :         zebra_nhg_release_all_deps(nhe);
    1126             : 
    1127             :         /*
    1128             :          * If its not zebra owned, we didn't store it here and have to be
    1129             :          * sure we don't clear one thats actually being used.
    1130             :          */
    1131          13 :         if (nhe->id < ZEBRA_NHG_PROTO_LOWER)
    1132          13 :                 hash_release(zrouter.nhgs, nhe);
    1133             : 
    1134          13 :         hash_release(zrouter.nhgs_id, nhe);
    1135          13 : }
    1136             : 
    1137          13 : static void zebra_nhg_handle_uninstall(struct nhg_hash_entry *nhe)
    1138             : {
    1139          13 :         zebra_nhg_release(nhe);
    1140          13 :         zebra_nhg_free(nhe);
    1141           0 : }
    1142             : 
    1143           4 : static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe)
    1144             : {
    1145             :         /* Update validity of groups depending on it */
    1146           4 :         struct nhg_connected *rb_node_dep;
    1147             : 
    1148           8 :         frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
    1149           0 :                 zebra_nhg_set_valid(rb_node_dep->nhe);
    1150           4 : }
    1151             : 
    1152             : /*
    1153             :  * The kernel/other program has changed the state of a nexthop object we are
    1154             :  * using.
    1155             :  */
    1156           0 : static void zebra_nhg_handle_kernel_state_change(struct nhg_hash_entry *nhe,
    1157             :                                                  bool is_delete)
    1158             : {
    1159           0 :         if (nhe->refcnt) {
    1160           0 :                 flog_err(
    1161             :                         EC_ZEBRA_NHG_SYNC,
    1162             :                         "Kernel %s a nexthop group with ID (%pNG) that we are still using for a route, sending it back down",
    1163             :                         (is_delete ? "deleted" : "updated"), nhe);
    1164             : 
    1165           0 :                 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
    1166           0 :                 zebra_nhg_install_kernel(nhe);
    1167             :         } else
    1168           0 :                 zebra_nhg_handle_uninstall(nhe);
    1169           0 : }
    1170             : 
    1171           0 : static int nhg_ctx_process_new(struct nhg_ctx *ctx)
    1172             : {
    1173           0 :         struct nexthop_group *nhg = NULL;
    1174           0 :         struct nhg_connected_tree_head nhg_depends = {};
    1175           0 :         struct nhg_hash_entry *lookup = NULL;
    1176           0 :         struct nhg_hash_entry *nhe = NULL;
    1177             : 
    1178           0 :         uint32_t id = nhg_ctx_get_id(ctx);
    1179           0 :         uint8_t count = nhg_ctx_get_count(ctx);
    1180           0 :         vrf_id_t vrf_id = nhg_ctx_get_vrf_id(ctx);
    1181           0 :         int type = nhg_ctx_get_type(ctx);
    1182           0 :         afi_t afi = nhg_ctx_get_afi(ctx);
    1183             : 
    1184           0 :         lookup = zebra_nhg_lookup_id(id);
    1185             : 
    1186           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1187           0 :                 zlog_debug("%s: id %u, count %d, lookup => %p",
    1188             :                            __func__, id, count, lookup);
    1189             : 
    1190           0 :         if (lookup) {
    1191             :                 /* This is already present in our table, hence an update
    1192             :                  * that we did not initate.
    1193             :                  */
    1194           0 :                 zebra_nhg_handle_kernel_state_change(lookup, false);
    1195           0 :                 return 0;
    1196             :         }
    1197             : 
    1198           0 :         if (nhg_ctx_get_count(ctx)) {
    1199           0 :                 nhg = nexthop_group_new();
    1200           0 :                 if (zebra_nhg_process_grp(nhg, &nhg_depends,
    1201             :                                           nhg_ctx_get_grp(ctx), count,
    1202             :                                           nhg_ctx_get_resilience(ctx))) {
    1203           0 :                         depends_decrement_free(&nhg_depends);
    1204           0 :                         nexthop_group_delete(&nhg);
    1205           0 :                         return -ENOENT;
    1206             :                 }
    1207             : 
    1208           0 :                 if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, afi,
    1209             :                                     type, true))
    1210           0 :                         depends_decrement_free(&nhg_depends);
    1211             : 
    1212             :                 /* These got copied over in zebra_nhg_alloc() */
    1213           0 :                 nexthop_group_delete(&nhg);
    1214             :         } else
    1215           0 :                 nhe = zebra_nhg_find_nexthop(id, nhg_ctx_get_nh(ctx), afi, type,
    1216             :                                              true);
    1217             : 
    1218           0 :         if (!nhe) {
    1219           0 :                 flog_err(
    1220             :                         EC_ZEBRA_TABLE_LOOKUP_FAILED,
    1221             :                         "Zebra failed to find or create a nexthop hash entry for ID (%u)",
    1222             :                         id);
    1223           0 :                 return -1;
    1224             :         }
    1225             : 
    1226           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1227           0 :                 zlog_debug("%s: nhe %p (%pNG) is new", __func__, nhe, nhe);
    1228             : 
    1229             :         /*
    1230             :          * If daemon nhg from the kernel, add a refcnt here to indicate the
    1231             :          * daemon owns it.
    1232             :          */
    1233           0 :         if (PROTO_OWNED(nhe))
    1234           0 :                 zebra_nhg_increment_ref(nhe);
    1235             : 
    1236           0 :         SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
    1237           0 :         SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
    1238             : 
    1239           0 :         return 0;
    1240             : }
    1241             : 
    1242           0 : static int nhg_ctx_process_del(struct nhg_ctx *ctx)
    1243             : {
    1244           0 :         struct nhg_hash_entry *nhe = NULL;
    1245           0 :         uint32_t id = nhg_ctx_get_id(ctx);
    1246             : 
    1247           0 :         nhe = zebra_nhg_lookup_id(id);
    1248             : 
    1249           0 :         if (!nhe) {
    1250           0 :                 flog_warn(
    1251             :                         EC_ZEBRA_BAD_NHG_MESSAGE,
    1252             :                         "Kernel delete message received for nexthop group ID (%u) that we do not have in our ID table",
    1253             :                         id);
    1254           0 :                 return -1;
    1255             :         }
    1256             : 
    1257           0 :         zebra_nhg_handle_kernel_state_change(nhe, true);
    1258             : 
    1259           0 :         return 0;
    1260             : }
    1261             : 
    1262           0 : static void nhg_ctx_fini(struct nhg_ctx **ctx)
    1263             : {
    1264             :         /*
    1265             :          * Just freeing for now, maybe do something more in the future
    1266             :          * based on flag.
    1267             :          */
    1268             : 
    1269           0 :         nhg_ctx_free(ctx);
    1270             : }
    1271             : 
    1272           0 : static int queue_add(struct nhg_ctx *ctx)
    1273             : {
    1274             :         /* If its queued or already processed do nothing */
    1275           0 :         if (nhg_ctx_get_status(ctx) == NHG_CTX_QUEUED)
    1276             :                 return 0;
    1277             : 
    1278           0 :         if (rib_queue_nhg_ctx_add(ctx)) {
    1279           0 :                 nhg_ctx_set_status(ctx, NHG_CTX_FAILURE);
    1280           0 :                 return -1;
    1281             :         }
    1282             : 
    1283           0 :         nhg_ctx_set_status(ctx, NHG_CTX_QUEUED);
    1284             : 
    1285           0 :         return 0;
    1286             : }
    1287             : 
    1288           0 : int nhg_ctx_process(struct nhg_ctx *ctx)
    1289             : {
    1290           0 :         int ret = 0;
    1291             : 
    1292           0 :         switch (nhg_ctx_get_op(ctx)) {
    1293           0 :         case NHG_CTX_OP_NEW:
    1294           0 :                 ret = nhg_ctx_process_new(ctx);
    1295           0 :                 if (nhg_ctx_get_count(ctx) && ret == -ENOENT
    1296           0 :                     && nhg_ctx_get_status(ctx) != NHG_CTX_REQUEUED) {
    1297             :                         /**
    1298             :                          * We have entered a situation where we are
    1299             :                          * processing a group from the kernel
    1300             :                          * that has a contained nexthop which
    1301             :                          * we have not yet processed.
    1302             :                          *
    1303             :                          * Re-enqueue this ctx to be handled exactly one
    1304             :                          * more time (indicated by the flag).
    1305             :                          *
    1306             :                          * By the time we get back to it, we
    1307             :                          * should have processed its depends.
    1308             :                          */
    1309           0 :                         nhg_ctx_set_status(ctx, NHG_CTX_NONE);
    1310           0 :                         if (queue_add(ctx) == 0) {
    1311           0 :                                 nhg_ctx_set_status(ctx, NHG_CTX_REQUEUED);
    1312           0 :                                 return 0;
    1313             :                         }
    1314             :                 }
    1315             :                 break;
    1316           0 :         case NHG_CTX_OP_DEL:
    1317           0 :                 ret = nhg_ctx_process_del(ctx);
    1318             :         case NHG_CTX_OP_NONE:
    1319             :                 break;
    1320             :         }
    1321             : 
    1322           0 :         nhg_ctx_set_status(ctx, (ret ? NHG_CTX_FAILURE : NHG_CTX_SUCCESS));
    1323             : 
    1324           0 :         nhg_ctx_fini(&ctx);
    1325             : 
    1326           0 :         return ret;
    1327             : }
    1328             : 
    1329             : /* Kernel-side, you either get a single new nexthop or a array of ID's */
    1330           0 : int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp,
    1331             :                           uint8_t count, vrf_id_t vrf_id, afi_t afi, int type,
    1332             :                           int startup, struct nhg_resilience *nhgr)
    1333             : {
    1334           0 :         struct nhg_ctx *ctx = NULL;
    1335             : 
    1336           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1337           0 :                 zlog_debug("%s: nh %pNHv, id %u, count %d",
    1338             :                            __func__, nh, id, (int)count);
    1339             : 
    1340           0 :         if (id > id_counter && id < ZEBRA_NHG_PROTO_LOWER)
    1341             :                 /* Increase our counter so we don't try to create
    1342             :                  * an ID that already exists
    1343             :                  */
    1344           0 :                 id_counter = id;
    1345             : 
    1346           0 :         ctx = nhg_ctx_init(id, nh, grp, vrf_id, afi, type, count, nhgr);
    1347           0 :         nhg_ctx_set_op(ctx, NHG_CTX_OP_NEW);
    1348             : 
    1349             :         /* Under statup conditions, we need to handle them immediately
    1350             :          * like we do for routes. Otherwise, we are going to get a route
    1351             :          * with a nhe_id that we have not handled.
    1352             :          */
    1353           0 :         if (startup)
    1354           0 :                 return nhg_ctx_process(ctx);
    1355             : 
    1356           0 :         if (queue_add(ctx)) {
    1357           0 :                 nhg_ctx_fini(&ctx);
    1358           0 :                 return -1;
    1359             :         }
    1360             : 
    1361             :         return 0;
    1362             : }
    1363             : 
    1364             : /* Kernel-side, received delete message */
    1365           0 : int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id)
    1366             : {
    1367           0 :         struct nhg_ctx *ctx = NULL;
    1368             : 
    1369           0 :         ctx = nhg_ctx_init(id, NULL, NULL, vrf_id, 0, 0, 0, NULL);
    1370             : 
    1371           0 :         nhg_ctx_set_op(ctx, NHG_CTX_OP_DEL);
    1372             : 
    1373           0 :         if (queue_add(ctx)) {
    1374           0 :                 nhg_ctx_fini(&ctx);
    1375           0 :                 return -1;
    1376             :         }
    1377             : 
    1378             :         return 0;
    1379             : }
    1380             : 
    1381             : /* Some dependency helper functions */
    1382           0 : static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh,
    1383             :                                                      afi_t afi, int type)
    1384             : {
    1385           0 :         struct nhg_hash_entry *nhe;
    1386           0 :         struct nexthop *lookup = NULL;
    1387             : 
    1388           0 :         lookup = nexthop_dup(nh, NULL);
    1389             : 
    1390           0 :         nhe = zebra_nhg_find_nexthop(0, lookup, afi, type, false);
    1391             : 
    1392           0 :         nexthops_free(lookup);
    1393             : 
    1394           0 :         return nhe;
    1395             : }
    1396             : 
    1397           0 : static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh,
    1398             :                                                      afi_t afi, int type,
    1399             :                                                      bool from_dplane)
    1400             : {
    1401           0 :         struct nhg_hash_entry *nhe;
    1402           0 :         struct nexthop lookup = {};
    1403             : 
    1404             :         /* Capture a snapshot of this single nh; it might be part of a list,
    1405             :          * so we need to make a standalone copy.
    1406             :          */
    1407           0 :         nexthop_copy_no_recurse(&lookup, nh, NULL);
    1408             : 
    1409           0 :         nhe = zebra_nhg_find_nexthop(0, &lookup, afi, type, from_dplane);
    1410             : 
    1411             :         /* The copy may have allocated labels; free them if necessary. */
    1412           0 :         nexthop_del_labels(&lookup);
    1413           0 :         nexthop_del_srv6_seg6local(&lookup);
    1414           0 :         nexthop_del_srv6_seg6(&lookup);
    1415             : 
    1416           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1417           0 :                 zlog_debug("%s: nh %pNHv => %p (%pNG)", __func__, nh, nhe, nhe);
    1418             : 
    1419           0 :         return nhe;
    1420             : }
    1421             : 
    1422           0 : static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi,
    1423             :                                            int type, bool from_dplane)
    1424             : {
    1425           0 :         struct nhg_hash_entry *nhe = NULL;
    1426             : 
    1427           0 :         if (!nh)
    1428           0 :                 goto done;
    1429             : 
    1430             :         /* We are separating these functions out to increase handling speed
    1431             :          * in the non-recursive case (by not alloc/freeing)
    1432             :          */
    1433           0 :         if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE))
    1434           0 :                 nhe = depends_find_recursive(nh, afi, type);
    1435             :         else
    1436           0 :                 nhe = depends_find_singleton(nh, afi, type, from_dplane);
    1437             : 
    1438             : 
    1439           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL) {
    1440           0 :                 zlog_debug("%s: nh %pNHv %s => %p (%pNG)", __func__, nh,
    1441             :                            CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE) ? "(R)"
    1442             :                                                                          : "",
    1443             :                            nhe, nhe);
    1444             :         }
    1445             : 
    1446           0 : done:
    1447           0 :         return nhe;
    1448             : }
    1449             : 
    1450           0 : static void depends_add(struct nhg_connected_tree_head *head,
    1451             :                         struct nhg_hash_entry *depend)
    1452             : {
    1453           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1454           0 :                 zlog_debug("%s: head %p nh %pNHv",
    1455             :                            __func__, head, depend->nhg.nexthop);
    1456             : 
    1457             :         /* If NULL is returned, it was successfully added and
    1458             :          * needs to have its refcnt incremented.
    1459             :          *
    1460             :          * Else the NHE is already present in the tree and doesn't
    1461             :          * need to increment the refcnt.
    1462             :          */
    1463           0 :         if (nhg_connected_tree_add_nhe(head, depend) == NULL)
    1464           0 :                 zebra_nhg_increment_ref(depend);
    1465           0 : }
    1466             : 
    1467             : static struct nhg_hash_entry *
    1468           0 : depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh,
    1469             :                  afi_t afi, int type, bool from_dplane)
    1470             : {
    1471           0 :         struct nhg_hash_entry *depend = NULL;
    1472             : 
    1473           0 :         depend = depends_find(nh, afi, type, from_dplane);
    1474             : 
    1475           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1476           0 :                 zlog_debug("%s: nh %pNHv => %p",
    1477             :                            __func__, nh, depend);
    1478             : 
    1479           0 :         if (depend)
    1480           0 :                 depends_add(head, depend);
    1481             : 
    1482           0 :         return depend;
    1483             : }
    1484             : 
    1485             : static struct nhg_hash_entry *
    1486           0 : depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id)
    1487             : {
    1488           0 :         struct nhg_hash_entry *depend = NULL;
    1489             : 
    1490           0 :         depend = zebra_nhg_lookup_id(id);
    1491             : 
    1492           0 :         if (depend)
    1493           0 :                 depends_add(head, depend);
    1494             : 
    1495           0 :         return depend;
    1496             : }
    1497             : 
    1498           0 : static void depends_decrement_free(struct nhg_connected_tree_head *head)
    1499             : {
    1500           0 :         nhg_connected_tree_decrement_ref(head);
    1501           0 :         nhg_connected_tree_free(head);
    1502           0 : }
    1503             : 
    1504             : /* Find an nhe based on a list of nexthops */
    1505           0 : struct nhg_hash_entry *zebra_nhg_rib_find(uint32_t id,
    1506             :                                           struct nexthop_group *nhg,
    1507             :                                           afi_t rt_afi, int type)
    1508             : {
    1509           0 :         struct nhg_hash_entry *nhe = NULL;
    1510           0 :         vrf_id_t vrf_id;
    1511             : 
    1512             :         /*
    1513             :          * CLANG SA is complaining that nexthop may be NULL
    1514             :          * Make it happy but this is ridonc
    1515             :          */
    1516           0 :         assert(nhg->nexthop);
    1517           0 :         vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nhg->nexthop->vrf_id;
    1518             : 
    1519           0 :         zebra_nhg_find(&nhe, id, nhg, NULL, vrf_id, rt_afi, type, false);
    1520             : 
    1521           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1522           0 :                 zlog_debug("%s: => nhe %p (%pNG)", __func__, nhe, nhe);
    1523             : 
    1524           0 :         return nhe;
    1525             : }
    1526             : 
    1527             : /* Find an nhe based on a route's nhe */
    1528             : struct nhg_hash_entry *
    1529          32 : zebra_nhg_rib_find_nhe(struct nhg_hash_entry *rt_nhe, afi_t rt_afi)
    1530             : {
    1531          32 :         struct nhg_hash_entry *nhe = NULL;
    1532             : 
    1533          32 :         if (!(rt_nhe && rt_nhe->nhg.nexthop)) {
    1534           0 :                 flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED,
    1535             :                          "No nexthop passed to %s", __func__);
    1536           0 :                 return NULL;
    1537             :         }
    1538             : 
    1539          32 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1540           0 :                 zlog_debug("%s: rt_nhe %p (%pNG)", __func__, rt_nhe, rt_nhe);
    1541             : 
    1542          32 :         zebra_nhe_find(&nhe, rt_nhe, NULL, rt_afi, false);
    1543             : 
    1544          32 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1545           0 :                 zlog_debug("%s: => nhe %p (%pNG)", __func__, nhe, nhe);
    1546             : 
    1547          32 :         return nhe;
    1548             : }
    1549             : 
    1550             : /*
    1551             :  * Allocate backup nexthop info object. Typically these are embedded in
    1552             :  * nhg_hash_entry objects.
    1553             :  */
    1554           0 : struct nhg_backup_info *zebra_nhg_backup_alloc(void)
    1555             : {
    1556           0 :         struct nhg_backup_info *p;
    1557             : 
    1558           0 :         p = XCALLOC(MTYPE_NHG, sizeof(struct nhg_backup_info));
    1559             : 
    1560           0 :         p->nhe = zebra_nhg_alloc();
    1561             : 
    1562             :         /* Identify the embedded group used to hold the list of backups */
    1563           0 :         SET_FLAG(p->nhe->flags, NEXTHOP_GROUP_BACKUP);
    1564             : 
    1565           0 :         return p;
    1566             : }
    1567             : 
    1568             : /*
    1569             :  * Free backup nexthop info object, deal with any embedded allocations
    1570             :  */
    1571          45 : void zebra_nhg_backup_free(struct nhg_backup_info **p)
    1572             : {
    1573          45 :         if (p && *p) {
    1574           0 :                 if ((*p)->nhe)
    1575           0 :                         zebra_nhg_free((*p)->nhe);
    1576             : 
    1577           0 :                 XFREE(MTYPE_NHG, (*p));
    1578             :         }
    1579          45 : }
    1580             : 
    1581             : /* Accessor for backup nexthop group */
    1582          37 : struct nexthop_group *zebra_nhg_get_backup_nhg(struct nhg_hash_entry *nhe)
    1583             : {
    1584          53 :         struct nexthop_group *p = NULL;
    1585             : 
    1586          24 :         if (nhe) {
    1587          37 :                 if (nhe->backup_info && nhe->backup_info->nhe)
    1588           0 :                         p = &(nhe->backup_info->nhe->nhg);
    1589             :         }
    1590             : 
    1591           0 :         return p;
    1592             : }
    1593             : 
    1594             : /*
    1595             :  * Helper to return a copy of a backup_info - note that this is a shallow
    1596             :  * copy, meant to be used when creating a new nhe from info passed in with
    1597             :  * a route e.g.
    1598             :  */
    1599             : static struct nhg_backup_info *
    1600           0 : nhg_backup_copy(const struct nhg_backup_info *orig)
    1601             : {
    1602           0 :         struct nhg_backup_info *b;
    1603             : 
    1604           0 :         b = zebra_nhg_backup_alloc();
    1605             : 
    1606             :         /* Copy list of nexthops */
    1607           0 :         nexthop_group_copy(&(b->nhe->nhg), &(orig->nhe->nhg));
    1608             : 
    1609           0 :         return b;
    1610             : }
    1611             : 
    1612          45 : static void zebra_nhg_free_members(struct nhg_hash_entry *nhe)
    1613             : {
    1614          45 :         nexthops_free(nhe->nhg.nexthop);
    1615             : 
    1616          45 :         zebra_nhg_backup_free(&nhe->backup_info);
    1617             : 
    1618             :         /* Decrement to remove connection ref */
    1619          45 :         nhg_connected_tree_decrement_ref(&nhe->nhg_depends);
    1620          45 :         nhg_connected_tree_free(&nhe->nhg_depends);
    1621          45 :         nhg_connected_tree_free(&nhe->nhg_dependents);
    1622          45 : }
    1623             : 
    1624          45 : void zebra_nhg_free(struct nhg_hash_entry *nhe)
    1625             : {
    1626          45 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL) {
    1627             :                 /* Group or singleton? */
    1628           0 :                 if (nhe->nhg.nexthop && nhe->nhg.nexthop->next)
    1629           0 :                         zlog_debug("%s: nhe %p (%pNG), refcnt %d", __func__,
    1630             :                                    nhe, nhe, nhe->refcnt);
    1631             :                 else
    1632           0 :                         zlog_debug("%s: nhe %p (%pNG), refcnt %d, NH %pNHv",
    1633             :                                    __func__, nhe, nhe, nhe->refcnt,
    1634             :                                    nhe->nhg.nexthop);
    1635             :         }
    1636             : 
    1637          45 :         THREAD_OFF(nhe->timer);
    1638             : 
    1639          45 :         zebra_nhg_free_members(nhe);
    1640             : 
    1641          45 :         XFREE(MTYPE_NHG, nhe);
    1642          45 : }
    1643             : 
    1644             : /*
    1645             :  * Let's just drop the memory associated with each item
    1646             :  */
    1647           0 : void zebra_nhg_hash_free(void *p)
    1648             : {
    1649           0 :         struct nhg_hash_entry *nhe = p;
    1650             : 
    1651           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL) {
    1652             :                 /* Group or singleton? */
    1653           0 :                 if (nhe->nhg.nexthop && nhe->nhg.nexthop->next)
    1654           0 :                         zlog_debug("%s: nhe %p (%u), refcnt %d", __func__, nhe,
    1655             :                                    nhe->id, nhe->refcnt);
    1656             :                 else
    1657           0 :                         zlog_debug("%s: nhe %p (%pNG), refcnt %d, NH %pNHv",
    1658             :                                    __func__, nhe, nhe, nhe->refcnt,
    1659             :                                    nhe->nhg.nexthop);
    1660             :         }
    1661             : 
    1662           0 :         THREAD_OFF(nhe->timer);
    1663             : 
    1664           0 :         nexthops_free(nhe->nhg.nexthop);
    1665             : 
    1666           0 :         XFREE(MTYPE_NHG, nhe);
    1667           0 : }
    1668             : 
    1669             : /*
    1670             :  * On cleanup there are nexthop groups that have not
    1671             :  * been resolved at all( a nhe->id of 0 ).  As such
    1672             :  * zebra needs to clean up the memory associated with
    1673             :  * those entries.
    1674             :  */
    1675           0 : void zebra_nhg_hash_free_zero_id(struct hash_bucket *b, void *arg)
    1676             : {
    1677           0 :         struct nhg_hash_entry *nhe = b->data;
    1678           0 :         struct nhg_connected *dep;
    1679             : 
    1680           0 :         while ((dep = nhg_connected_tree_pop(&nhe->nhg_depends))) {
    1681           0 :                 if (dep->nhe->id == 0)
    1682           0 :                         zebra_nhg_hash_free(dep->nhe);
    1683             : 
    1684           0 :                 nhg_connected_free(dep);
    1685             :         }
    1686             : 
    1687           0 :         while ((dep = nhg_connected_tree_pop(&nhe->nhg_dependents)))
    1688           0 :                 nhg_connected_free(dep);
    1689             : 
    1690           0 :         if (nhe->backup_info && nhe->backup_info->nhe->id == 0) {
    1691           0 :                 while ((dep = nhg_connected_tree_pop(
    1692           0 :                                 &nhe->backup_info->nhe->nhg_depends)))
    1693           0 :                         nhg_connected_free(dep);
    1694             : 
    1695           0 :                 zebra_nhg_hash_free(nhe->backup_info->nhe);
    1696             : 
    1697           0 :                 XFREE(MTYPE_NHG, nhe->backup_info);
    1698             :         }
    1699           0 : }
    1700             : 
    1701           0 : static void zebra_nhg_timer(struct thread *thread)
    1702             : {
    1703           0 :         struct nhg_hash_entry *nhe = THREAD_ARG(thread);
    1704             : 
    1705           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1706           0 :                 zlog_debug("Nexthop Timer for nhe: %pNG", nhe);
    1707             : 
    1708           0 :         if (nhe->refcnt == 1)
    1709           0 :                 zebra_nhg_decrement_ref(nhe);
    1710           0 : }
    1711             : 
    1712          32 : void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
    1713             : {
    1714          32 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1715           0 :                 zlog_debug("%s: nhe %p (%pNG) %d => %d", __func__, nhe, nhe,
    1716             :                            nhe->refcnt, nhe->refcnt - 1);
    1717             : 
    1718          32 :         nhe->refcnt--;
    1719             : 
    1720          32 :         if (!zebra_router_in_shutdown() && nhe->refcnt <= 0 &&
    1721           7 :             CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) &&
    1722             :             !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND)) {
    1723           0 :                 nhe->refcnt = 1;
    1724           0 :                 SET_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND);
    1725           0 :                 thread_add_timer(zrouter.master, zebra_nhg_timer, nhe,
    1726             :                                  zrouter.nhg_keep, &nhe->timer);
    1727           0 :                 return;
    1728             :         }
    1729             : 
    1730          32 :         if (!zebra_nhg_depends_is_empty(nhe))
    1731           0 :                 nhg_connected_tree_decrement_ref(&nhe->nhg_depends);
    1732             : 
    1733          32 :         if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0)
    1734          13 :                 zebra_nhg_uninstall_kernel(nhe);
    1735             : }
    1736             : 
    1737          32 : void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
    1738             : {
    1739          32 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    1740           0 :                 zlog_debug("%s: nhe %p (%pNG) %d => %d", __func__, nhe, nhe,
    1741             :                            nhe->refcnt, nhe->refcnt + 1);
    1742             : 
    1743          32 :         nhe->refcnt++;
    1744             : 
    1745          32 :         if (thread_is_scheduled(nhe->timer)) {
    1746           0 :                 THREAD_OFF(nhe->timer);
    1747           0 :                 nhe->refcnt--;
    1748           0 :                 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND);
    1749             :         }
    1750             : 
    1751          32 :         if (!zebra_nhg_depends_is_empty(nhe))
    1752           0 :                 nhg_connected_tree_increment_ref(&nhe->nhg_depends);
    1753          32 : }
    1754             : 
    1755           0 : static struct nexthop *nexthop_set_resolved(afi_t afi,
    1756             :                                             const struct nexthop *newhop,
    1757             :                                             struct nexthop *nexthop,
    1758             :                                             struct zebra_sr_policy *policy)
    1759             : {
    1760           0 :         struct nexthop *resolved_hop;
    1761           0 :         uint8_t num_labels = 0;
    1762           0 :         mpls_label_t labels[MPLS_MAX_LABELS];
    1763           0 :         enum lsp_types_t label_type = ZEBRA_LSP_NONE;
    1764           0 :         int i = 0;
    1765             : 
    1766           0 :         resolved_hop = nexthop_new();
    1767           0 :         SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
    1768             : 
    1769           0 :         resolved_hop->vrf_id = nexthop->vrf_id;
    1770           0 :         switch (newhop->type) {
    1771           0 :         case NEXTHOP_TYPE_IPV4:
    1772             :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1773             :                 /* If the resolving route specifies a gateway, use it */
    1774           0 :                 resolved_hop->type = newhop->type;
    1775           0 :                 resolved_hop->gate.ipv4 = newhop->gate.ipv4;
    1776             : 
    1777           0 :                 if (newhop->ifindex) {
    1778           0 :                         resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
    1779           0 :                         resolved_hop->ifindex = newhop->ifindex;
    1780             :                 }
    1781             :                 break;
    1782           0 :         case NEXTHOP_TYPE_IPV6:
    1783             :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1784           0 :                 resolved_hop->type = newhop->type;
    1785           0 :                 resolved_hop->gate.ipv6 = newhop->gate.ipv6;
    1786             : 
    1787           0 :                 if (newhop->ifindex) {
    1788           0 :                         resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
    1789           0 :                         resolved_hop->ifindex = newhop->ifindex;
    1790             :                 }
    1791             :                 break;
    1792           0 :         case NEXTHOP_TYPE_IFINDEX:
    1793             :                 /* If the resolving route is an interface route,
    1794             :                  * it means the gateway we are looking up is connected
    1795             :                  * to that interface. (The actual network is _not_ onlink).
    1796             :                  * Therefore, the resolved route should have the original
    1797             :                  * gateway as nexthop as it is directly connected.
    1798             :                  *
    1799             :                  * On Linux, we have to set the onlink netlink flag because
    1800             :                  * otherwise, the kernel won't accept the route.
    1801             :                  */
    1802           0 :                 resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
    1803           0 :                 if (afi == AFI_IP) {
    1804           0 :                         resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
    1805           0 :                         resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
    1806           0 :                 } else if (afi == AFI_IP6) {
    1807           0 :                         resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
    1808           0 :                         resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
    1809             :                 }
    1810           0 :                 resolved_hop->ifindex = newhop->ifindex;
    1811           0 :                 break;
    1812           0 :         case NEXTHOP_TYPE_BLACKHOLE:
    1813           0 :                 resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
    1814           0 :                 resolved_hop->bh_type = newhop->bh_type;
    1815           0 :                 break;
    1816             :         }
    1817             : 
    1818           0 :         if (newhop->flags & NEXTHOP_FLAG_ONLINK)
    1819           0 :                 resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
    1820             : 
    1821             :         /* Copy labels of the resolved route and the parent resolving to it */
    1822           0 :         if (policy) {
    1823           0 :                 int label_num = 0;
    1824             : 
    1825             :                 /*
    1826             :                  * Don't push the first SID if the corresponding action in the
    1827             :                  * LFIB is POP.
    1828             :                  */
    1829           0 :                 if (!newhop->nh_label || !newhop->nh_label->num_labels
    1830           0 :                     || newhop->nh_label->label[0] == MPLS_LABEL_IMPLICIT_NULL)
    1831           0 :                         label_num = 1;
    1832             : 
    1833           0 :                 for (; label_num < policy->segment_list.label_num; label_num++)
    1834           0 :                         labels[num_labels++] =
    1835           0 :                                 policy->segment_list.labels[label_num];
    1836           0 :                 label_type = policy->segment_list.type;
    1837           0 :         } else if (newhop->nh_label) {
    1838           0 :                 for (i = 0; i < newhop->nh_label->num_labels; i++) {
    1839             :                         /* Be a bit picky about overrunning the local array */
    1840           0 :                         if (num_labels >= MPLS_MAX_LABELS) {
    1841           0 :                                 if (IS_ZEBRA_DEBUG_NHG || IS_ZEBRA_DEBUG_RIB)
    1842           0 :                                         zlog_debug("%s: too many labels in newhop %pNHv",
    1843             :                                                    __func__, newhop);
    1844             :                                 break;
    1845             :                         }
    1846           0 :                         labels[num_labels++] = newhop->nh_label->label[i];
    1847             :                 }
    1848             :                 /* Use the "outer" type */
    1849           0 :                 label_type = newhop->nh_label_type;
    1850             :         }
    1851             : 
    1852           0 :         if (nexthop->nh_label) {
    1853           0 :                 for (i = 0; i < nexthop->nh_label->num_labels; i++) {
    1854             :                         /* Be a bit picky about overrunning the local array */
    1855           0 :                         if (num_labels >= MPLS_MAX_LABELS) {
    1856           0 :                                 if (IS_ZEBRA_DEBUG_NHG || IS_ZEBRA_DEBUG_RIB)
    1857           0 :                                         zlog_debug("%s: too many labels in nexthop %pNHv",
    1858             :                                                    __func__, nexthop);
    1859             :                                 break;
    1860             :                         }
    1861           0 :                         labels[num_labels++] = nexthop->nh_label->label[i];
    1862             :                 }
    1863             : 
    1864             :                 /* If the parent has labels, use its type if
    1865             :                  * we don't already have one.
    1866             :                  */
    1867           0 :                 if (label_type == ZEBRA_LSP_NONE)
    1868           0 :                         label_type = nexthop->nh_label_type;
    1869             :         }
    1870             : 
    1871           0 :         if (num_labels)
    1872           0 :                 nexthop_add_labels(resolved_hop, label_type, num_labels,
    1873             :                                    labels);
    1874             : 
    1875           0 :         if (nexthop->nh_srv6) {
    1876           0 :                 nexthop_add_srv6_seg6local(resolved_hop,
    1877           0 :                                            nexthop->nh_srv6->seg6local_action,
    1878           0 :                                            &nexthop->nh_srv6->seg6local_ctx);
    1879           0 :                 nexthop_add_srv6_seg6(resolved_hop,
    1880           0 :                                       &nexthop->nh_srv6->seg6_segs);
    1881             :         }
    1882             : 
    1883           0 :         resolved_hop->rparent = nexthop;
    1884           0 :         _nexthop_add(&nexthop->resolved, resolved_hop);
    1885             : 
    1886           0 :         return resolved_hop;
    1887             : }
    1888             : 
    1889             : /* Checks if nexthop we are trying to resolve to is valid */
    1890           0 : static bool nexthop_valid_resolve(const struct nexthop *nexthop,
    1891             :                                   const struct nexthop *resolved)
    1892             : {
    1893             :         /* Can't resolve to a recursive nexthop */
    1894           0 :         if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE))
    1895             :                 return false;
    1896             : 
    1897             :         /* Must be ACTIVE */
    1898           0 :         if (!CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_ACTIVE))
    1899             :                 return false;
    1900             : 
    1901             :         /* Must not be duplicate */
    1902           0 :         if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_DUPLICATE))
    1903             :                 return false;
    1904             : 
    1905           0 :         switch (nexthop->type) {
    1906           0 :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1907             :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1908             :                 /* If the nexthop we are resolving to does not match the
    1909             :                  * ifindex for the nexthop the route wanted, its not valid.
    1910             :                  */
    1911           0 :                 if (nexthop->ifindex != resolved->ifindex)
    1912           0 :                         return false;
    1913             :                 break;
    1914             :         case NEXTHOP_TYPE_IPV4:
    1915             :         case NEXTHOP_TYPE_IPV6:
    1916             :         case NEXTHOP_TYPE_IFINDEX:
    1917             :         case NEXTHOP_TYPE_BLACKHOLE:
    1918             :                 break;
    1919             :         }
    1920             : 
    1921             :         return true;
    1922             : }
    1923             : 
    1924             : /*
    1925             :  * When resolving a recursive nexthop, capture backup nexthop(s) also
    1926             :  * so they can be conveyed through the dataplane to the FIB. We'll look
    1927             :  * at the backups in the resolving nh 'nexthop' and its nhe, and copy them
    1928             :  * into the route's resolved nh 'resolved' and its nhe 'nhe'.
    1929             :  */
    1930           0 : static int resolve_backup_nexthops(const struct nexthop *nexthop,
    1931             :                                    const struct nhg_hash_entry *nhe,
    1932             :                                    struct nexthop *resolved,
    1933             :                                    struct nhg_hash_entry *resolve_nhe,
    1934             :                                    struct backup_nh_map_s *map)
    1935             : {
    1936           0 :         int i, j, idx;
    1937           0 :         const struct nexthop *bnh;
    1938           0 :         struct nexthop *nh, *newnh;
    1939           0 :         mpls_label_t labels[MPLS_MAX_LABELS];
    1940           0 :         uint8_t num_labels;
    1941             : 
    1942           0 :         assert(nexthop->backup_num <= NEXTHOP_MAX_BACKUPS);
    1943             : 
    1944             :         /* Locate backups from the original nexthop's backup index and nhe */
    1945           0 :         for (i = 0; i < nexthop->backup_num; i++) {
    1946           0 :                 idx = nexthop->backup_idx[i];
    1947             : 
    1948             :                 /* Do we already know about this particular backup? */
    1949           0 :                 for (j = 0; j < map->map_count; j++) {
    1950           0 :                         if (map->map[j].orig_idx == idx)
    1951             :                                 break;
    1952             :                 }
    1953             : 
    1954           0 :                 if (j < map->map_count) {
    1955           0 :                         resolved->backup_idx[resolved->backup_num] =
    1956           0 :                                 map->map[j].new_idx;
    1957           0 :                         resolved->backup_num++;
    1958             : 
    1959           0 :                         SET_FLAG(resolved->flags, NEXTHOP_FLAG_HAS_BACKUP);
    1960             : 
    1961           0 :                         if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    1962           0 :                                 zlog_debug("%s: found map idx orig %d, new %d",
    1963             :                                            __func__, map->map[j].orig_idx,
    1964             :                                            map->map[j].new_idx);
    1965             : 
    1966           0 :                         continue;
    1967             :                 }
    1968             : 
    1969             :                 /* We can't handle any new map entries at this point. */
    1970           0 :                 if (map->map_count == MULTIPATH_NUM)
    1971             :                         break;
    1972             : 
    1973             :                 /* Need to create/copy a new backup */
    1974           0 :                 bnh = nhe->backup_info->nhe->nhg.nexthop;
    1975           0 :                 for (j = 0; j < idx; j++) {
    1976           0 :                         if (bnh == NULL)
    1977             :                                 break;
    1978           0 :                         bnh = bnh->next;
    1979             :                 }
    1980             : 
    1981             :                 /* Whoops - bad index in the nexthop? */
    1982           0 :                 if (bnh == NULL)
    1983           0 :                         continue;
    1984             : 
    1985           0 :                 if (resolve_nhe->backup_info == NULL)
    1986           0 :                         resolve_nhe->backup_info = zebra_nhg_backup_alloc();
    1987             : 
    1988             :                 /* Update backup info in the resolving nexthop and its nhe */
    1989           0 :                 newnh = nexthop_dup_no_recurse(bnh, NULL);
    1990             : 
    1991             :                 /* We may need some special handling for mpls labels: the new
    1992             :                  * backup needs to carry the recursive nexthop's labels,
    1993             :                  * if any: they may be vrf labels e.g.
    1994             :                  * The original/inner labels are in the stack of 'resolve_nhe',
    1995             :                  * if that is longer than the stack in 'nexthop'.
    1996             :                  */
    1997           0 :                 if (newnh->nh_label && resolved->nh_label &&
    1998           0 :                     nexthop->nh_label) {
    1999           0 :                         if (resolved->nh_label->num_labels >
    2000           0 :                             nexthop->nh_label->num_labels) {
    2001             :                                 /* Prepare new label stack */
    2002             :                                 num_labels = 0;
    2003           0 :                                 for (j = 0; j < newnh->nh_label->num_labels;
    2004           0 :                                      j++) {
    2005           0 :                                         labels[j] = newnh->nh_label->label[j];
    2006           0 :                                         num_labels++;
    2007             :                                 }
    2008             : 
    2009             :                                 /* Include inner labels */
    2010           0 :                                 for (j = nexthop->nh_label->num_labels;
    2011           0 :                                      j < resolved->nh_label->num_labels;
    2012           0 :                                      j++) {
    2013           0 :                                         labels[num_labels] =
    2014           0 :                                                 resolved->nh_label->label[j];
    2015           0 :                                         num_labels++;
    2016             :                                 }
    2017             : 
    2018             :                                 /* Replace existing label stack in the backup */
    2019           0 :                                 nexthop_del_labels(newnh);
    2020           0 :                                 nexthop_add_labels(newnh, bnh->nh_label_type,
    2021             :                                                    num_labels, labels);
    2022             :                         }
    2023             :                 }
    2024             : 
    2025             :                 /* Need to compute the new backup index in the new
    2026             :                  * backup list, and add to map struct.
    2027             :                  */
    2028           0 :                 j = 0;
    2029           0 :                 nh = resolve_nhe->backup_info->nhe->nhg.nexthop;
    2030           0 :                 if (nh) {
    2031           0 :                         while (nh->next) {
    2032           0 :                                 nh = nh->next;
    2033           0 :                                 j++;
    2034             :                         }
    2035             : 
    2036           0 :                         nh->next = newnh;
    2037           0 :                         j++;
    2038             : 
    2039             :                 } else  /* First one */
    2040           0 :                         resolve_nhe->backup_info->nhe->nhg.nexthop = newnh;
    2041             : 
    2042             :                 /* Capture index */
    2043           0 :                 resolved->backup_idx[resolved->backup_num] = j;
    2044           0 :                 resolved->backup_num++;
    2045             : 
    2046           0 :                 SET_FLAG(resolved->flags, NEXTHOP_FLAG_HAS_BACKUP);
    2047             : 
    2048           0 :                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2049           0 :                         zlog_debug("%s: added idx orig %d, new %d",
    2050             :                                    __func__, idx, j);
    2051             : 
    2052             :                 /* Update map/cache */
    2053           0 :                 map->map[map->map_count].orig_idx = idx;
    2054           0 :                 map->map[map->map_count].new_idx = j;
    2055           0 :                 map->map_count++;
    2056             :         }
    2057             : 
    2058           0 :         return 0;
    2059             : }
    2060             : 
    2061             : /*
    2062             :  * So this nexthop resolution has decided that a connected route
    2063             :  * is the correct choice.  At this point in time if FRR has multiple
    2064             :  * connected routes that all point to the same prefix one will be
    2065             :  * selected, *but* the particular interface may not be the one
    2066             :  * that the nexthop points at.  Let's look at all the available
    2067             :  * connected routes on this node and if any of them auto match
    2068             :  * the routes nexthops ifindex that is good enough for a match
    2069             :  *
    2070             :  * This code is depending on the fact that a nexthop->ifindex is 0
    2071             :  * if it is not known, if this assumption changes, yummy!
    2072             :  * Additionally a ifindx of 0 means figure it out for us.
    2073             :  */
    2074             : static struct route_entry *
    2075           0 : zebra_nhg_connected_ifindex(struct route_node *rn, struct route_entry *match,
    2076             :                             int32_t curr_ifindex)
    2077             : {
    2078           0 :         struct nexthop *newhop = match->nhe->nhg.nexthop;
    2079           0 :         struct route_entry *re;
    2080             : 
    2081           0 :         assert(newhop); /* What a kick in the patooey */
    2082             : 
    2083           0 :         if (curr_ifindex == 0)
    2084             :                 return match;
    2085             : 
    2086           0 :         if (curr_ifindex == newhop->ifindex)
    2087             :                 return match;
    2088             : 
    2089             :         /*
    2090             :          * At this point we know that this route is matching a connected
    2091             :          * but there are possibly a bunch of connected routes that are
    2092             :          * alive that should be considered as well.  So let's iterate over
    2093             :          * all the re's and see if they are connected as well and maybe one
    2094             :          * of those ifindexes match as well.
    2095             :          */
    2096           0 :         RNODE_FOREACH_RE (rn, re) {
    2097           0 :                 if (re->type != ZEBRA_ROUTE_CONNECT)
    2098           0 :                         continue;
    2099             : 
    2100           0 :                 if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
    2101           0 :                         continue;
    2102             : 
    2103             :                 /*
    2104             :                  * zebra has a connected route that is not removed
    2105             :                  * let's test if it is good
    2106             :                  */
    2107           0 :                 newhop = re->nhe->nhg.nexthop;
    2108           0 :                 assert(newhop);
    2109           0 :                 if (curr_ifindex == newhop->ifindex)
    2110           0 :                         return re;
    2111             :         }
    2112             : 
    2113             :         return match;
    2114             : }
    2115             : 
    2116             : /*
    2117             :  * Given a nexthop we need to properly recursively resolve,
    2118             :  * do a table lookup to find and match if at all possible.
    2119             :  * Set the nexthop->ifindex and resolution info as appropriate.
    2120             :  */
    2121          16 : static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe,
    2122             :                           const struct prefix *top, int type, uint32_t flags,
    2123             :                           uint32_t *pmtu, vrf_id_t vrf_id)
    2124             : {
    2125          16 :         struct prefix p;
    2126          16 :         struct route_table *table;
    2127          16 :         struct route_node *rn;
    2128          16 :         struct route_entry *match = NULL;
    2129          16 :         int resolved;
    2130          16 :         struct zebra_nhlfe *nhlfe;
    2131          16 :         struct nexthop *newhop;
    2132          16 :         struct interface *ifp;
    2133          16 :         rib_dest_t *dest;
    2134          16 :         struct zebra_vrf *zvrf;
    2135          16 :         struct in_addr local_ipv4;
    2136          16 :         struct in_addr *ipv4;
    2137          16 :         afi_t afi = AFI_IP;
    2138             : 
    2139             :         /* Reset some nexthop attributes that we'll recompute if necessary */
    2140          16 :         if ((nexthop->type == NEXTHOP_TYPE_IPV4)
    2141          16 :             || (nexthop->type == NEXTHOP_TYPE_IPV6))
    2142           0 :                 nexthop->ifindex = 0;
    2143             : 
    2144          16 :         UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
    2145          16 :         nexthops_free(nexthop->resolved);
    2146          16 :         nexthop->resolved = NULL;
    2147             : 
    2148             :         /*
    2149             :          * Set afi based on nexthop type.
    2150             :          * Some nexthop types get special handling, possibly skipping
    2151             :          * the normal processing.
    2152             :          */
    2153          16 :         switch (nexthop->type) {
    2154          16 :         case NEXTHOP_TYPE_IFINDEX:
    2155             : 
    2156          16 :                 ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
    2157             :                 /*
    2158             :                  * If the interface exists and its operative or its a kernel
    2159             :                  * route and interface is up, its active. We trust kernel routes
    2160             :                  * to be good.
    2161             :                  */
    2162          16 :                 if (ifp && (if_is_operative(ifp)))
    2163             :                         return 1;
    2164             :                 else
    2165           0 :                         return 0;
    2166           0 :                 break;
    2167             : 
    2168           0 :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    2169           0 :                 afi = AFI_IP6;
    2170             : 
    2171           0 :                 if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
    2172           0 :                         ifp = if_lookup_by_index(nexthop->ifindex,
    2173             :                                                  nexthop->vrf_id);
    2174           0 :                         if (ifp && if_is_operative(ifp))
    2175             :                                 return 1;
    2176             :                         else
    2177           0 :                                 return 0;
    2178             :                 }
    2179             :                 break;
    2180             : 
    2181             :         case NEXTHOP_TYPE_IPV4:
    2182             :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    2183             :                 afi = AFI_IP;
    2184             :                 break;
    2185             :         case NEXTHOP_TYPE_IPV6:
    2186           0 :                 afi = AFI_IP6;
    2187             :                 break;
    2188             : 
    2189             :         case NEXTHOP_TYPE_BLACKHOLE:
    2190             :                 return 1;
    2191             :         }
    2192             : 
    2193             :         /*
    2194             :          * If the nexthop has been marked as 'onlink' we just need to make
    2195             :          * sure the nexthop's interface is known and is operational.
    2196             :          */
    2197           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
    2198           0 :                 ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
    2199           0 :                 if (!ifp) {
    2200           0 :                         if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2201           0 :                                 zlog_debug("nexthop %pNHv marked onlink but nhif %u doesn't exist",
    2202             :                                            nexthop, nexthop->ifindex);
    2203           0 :                         return 0;
    2204             :                 }
    2205           0 :                 if (!if_is_operative(ifp)) {
    2206           0 :                         if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2207           0 :                                 zlog_debug("nexthop %pNHv marked onlink but nhif %s is not operational",
    2208             :                                            nexthop, ifp->name);
    2209           0 :                         return 0;
    2210             :                 }
    2211             :                 return 1;
    2212             :         }
    2213             : 
    2214           0 :         if (top &&
    2215           0 :             ((top->family == AF_INET && top->prefixlen == IPV4_MAX_BITLEN &&
    2216           0 :               nexthop->gate.ipv4.s_addr == top->u.prefix4.s_addr) ||
    2217           0 :              (top->family == AF_INET6 && top->prefixlen == IPV6_MAX_BITLEN &&
    2218           0 :               memcmp(&nexthop->gate.ipv6, &top->u.prefix6, IPV6_MAX_BYTELEN) ==
    2219           0 :                       0)) &&
    2220           0 :             nexthop->vrf_id == vrf_id) {
    2221           0 :                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2222           0 :                         zlog_debug(
    2223             :                                 "        :%s: Attempting to install a max prefixlength route through itself",
    2224             :                                 __func__);
    2225           0 :                 return 0;
    2226             :         }
    2227             : 
    2228             :         /* Validation for ipv4 mapped ipv6 nexthop. */
    2229           0 :         if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) {
    2230           0 :                 afi = AFI_IP;
    2231           0 :                 ipv4 = &local_ipv4;
    2232           0 :                 ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, ipv4);
    2233             :         } else {
    2234           0 :                 ipv4 = &nexthop->gate.ipv4;
    2235             :         }
    2236             : 
    2237             :         /* Processing for nexthops with SR 'color' attribute, using
    2238             :          * the corresponding SR policy object.
    2239             :          */
    2240           0 :         if (nexthop->srte_color) {
    2241           0 :                 struct ipaddr endpoint = {0};
    2242           0 :                 struct zebra_sr_policy *policy;
    2243             : 
    2244           0 :                 switch (afi) {
    2245           0 :                 case AFI_IP:
    2246           0 :                         endpoint.ipa_type = IPADDR_V4;
    2247           0 :                         endpoint.ipaddr_v4 = *ipv4;
    2248           0 :                         break;
    2249           0 :                 case AFI_IP6:
    2250           0 :                         endpoint.ipa_type = IPADDR_V6;
    2251           0 :                         endpoint.ipaddr_v6 = nexthop->gate.ipv6;
    2252           0 :                         break;
    2253             :                 case AFI_UNSPEC:
    2254             :                 case AFI_L2VPN:
    2255             :                 case AFI_MAX:
    2256             :                         flog_err(EC_LIB_DEVELOPMENT,
    2257             :                                  "%s: unknown address-family: %u", __func__,
    2258             :                                  afi);
    2259             :                         exit(1);
    2260             :                 }
    2261             : 
    2262           0 :                 policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint);
    2263           0 :                 if (policy && policy->status == ZEBRA_SR_POLICY_UP) {
    2264           0 :                         resolved = 0;
    2265           0 :                         frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list,
    2266             :                                        nhlfe) {
    2267           0 :                                 if (!CHECK_FLAG(nhlfe->flags,
    2268             :                                                 NHLFE_FLAG_SELECTED)
    2269           0 :                                     || CHECK_FLAG(nhlfe->flags,
    2270             :                                                   NHLFE_FLAG_DELETED))
    2271           0 :                                         continue;
    2272           0 :                                 SET_FLAG(nexthop->flags,
    2273             :                                          NEXTHOP_FLAG_RECURSIVE);
    2274           0 :                                 nexthop_set_resolved(afi, nhlfe->nexthop,
    2275             :                                                      nexthop, policy);
    2276           0 :                                 resolved = 1;
    2277             :                         }
    2278           0 :                         if (resolved)
    2279           0 :                                 return 1;
    2280             :                 }
    2281             :         }
    2282             : 
    2283             :         /* Make lookup prefix. */
    2284           0 :         memset(&p, 0, sizeof(struct prefix));
    2285           0 :         switch (afi) {
    2286           0 :         case AFI_IP:
    2287           0 :                 p.family = AF_INET;
    2288           0 :                 p.prefixlen = IPV4_MAX_BITLEN;
    2289           0 :                 p.u.prefix4 = *ipv4;
    2290           0 :                 break;
    2291           0 :         case AFI_IP6:
    2292           0 :                 p.family = AF_INET6;
    2293           0 :                 p.prefixlen = IPV6_MAX_BITLEN;
    2294           0 :                 p.u.prefix6 = nexthop->gate.ipv6;
    2295           0 :                 break;
    2296             :         case AFI_UNSPEC:
    2297             :         case AFI_L2VPN:
    2298             :         case AFI_MAX:
    2299             :                 assert(afi != AFI_IP && afi != AFI_IP6);
    2300             :                 break;
    2301             :         }
    2302             :         /* Lookup table.  */
    2303           0 :         table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id);
    2304             :         /* get zvrf */
    2305           0 :         zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
    2306           0 :         if (!table || !zvrf) {
    2307           0 :                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2308           0 :                         zlog_debug("        %s: Table not found", __func__);
    2309           0 :                 return 0;
    2310             :         }
    2311             : 
    2312           0 :         rn = route_node_match(table, (struct prefix *)&p);
    2313           0 :         while (rn) {
    2314           0 :                 route_unlock_node(rn);
    2315             : 
    2316             :                 /* Lookup should halt if we've matched against ourselves ('top',
    2317             :                  * if specified) - i.e., we cannot have a nexthop NH1 is
    2318             :                  * resolved by a route NH1. The exception is if the route is a
    2319             :                  * host route.
    2320             :                  */
    2321           0 :                 if (prefix_same(&rn->p, top))
    2322           0 :                         if (((afi == AFI_IP)
    2323           0 :                              && (rn->p.prefixlen != IPV4_MAX_BITLEN))
    2324           0 :                             || ((afi == AFI_IP6)
    2325           0 :                                 && (rn->p.prefixlen != IPV6_MAX_BITLEN))) {
    2326           0 :                                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2327           0 :                                         zlog_debug(
    2328             :                                                 "        %s: Matched against ourself and prefix length is not max bit length",
    2329             :                                                 __func__);
    2330           0 :                                 return 0;
    2331             :                         }
    2332             : 
    2333             :                 /* Pick up selected route. */
    2334             :                 /* However, do not resolve over default route unless explicitly
    2335             :                  * allowed.
    2336             :                  */
    2337           0 :                 if (is_default_prefix(&rn->p)
    2338           0 :                     && !rnh_resolve_via_default(zvrf, p.family)) {
    2339           0 :                         if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2340           0 :                                 zlog_debug(
    2341             :                                         "        :%s: Resolved against default route",
    2342             :                                         __func__);
    2343           0 :                         return 0;
    2344             :                 }
    2345             : 
    2346           0 :                 dest = rib_dest_from_rnode(rn);
    2347           0 :                 if (dest && dest->selected_fib
    2348           0 :                     && !CHECK_FLAG(dest->selected_fib->status,
    2349             :                                    ROUTE_ENTRY_REMOVED)
    2350           0 :                     && dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
    2351           0 :                         match = dest->selected_fib;
    2352             : 
    2353             :                 /* If there is no selected route or matched route is EGP, go up
    2354             :                  * tree.
    2355             :                  */
    2356           0 :                 if (!match) {
    2357           0 :                         do {
    2358           0 :                                 rn = rn->parent;
    2359           0 :                         } while (rn && rn->info == NULL);
    2360           0 :                         if (rn)
    2361           0 :                                 route_lock_node(rn);
    2362             : 
    2363           0 :                         continue;
    2364             :                 }
    2365             : 
    2366           0 :                 if ((match->type == ZEBRA_ROUTE_CONNECT) ||
    2367           0 :                     (RIB_SYSTEM_ROUTE(match) && RSYSTEM_ROUTE(type))) {
    2368           0 :                         match = zebra_nhg_connected_ifindex(rn, match,
    2369             :                                                             nexthop->ifindex);
    2370             : 
    2371           0 :                         newhop = match->nhe->nhg.nexthop;
    2372           0 :                         if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
    2373             :                             nexthop->type == NEXTHOP_TYPE_IPV6)
    2374           0 :                                 nexthop->ifindex = newhop->ifindex;
    2375           0 :                         else if (nexthop->ifindex != newhop->ifindex) {
    2376           0 :                                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2377           0 :                                         zlog_debug(
    2378             :                                                 "%s: %pNHv given ifindex does not match nexthops ifindex found: %pNHv",
    2379             :                                                 __func__, nexthop, newhop);
    2380             :                                 /*
    2381             :                                  * NEXTHOP_TYPE_*_IFINDEX but ifindex
    2382             :                                  * doesn't match what we found.
    2383             :                                  */
    2384           0 :                                 return 0;
    2385             :                         }
    2386             : 
    2387           0 :                         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2388           0 :                                 zlog_debug(
    2389             :                                         "%s: CONNECT match %p (%pNG), newhop %pNHv",
    2390             :                                         __func__, match, match->nhe, newhop);
    2391             : 
    2392           0 :                         return 1;
    2393           0 :                 } else if (CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
    2394           0 :                         struct nexthop_group *nhg;
    2395           0 :                         struct nexthop *resolver;
    2396           0 :                         struct backup_nh_map_s map = {};
    2397             : 
    2398           0 :                         resolved = 0;
    2399             : 
    2400             :                         /*
    2401             :                          * Only useful if installed or being Route Replacing
    2402             :                          * Why Being Route Replaced as well?
    2403             :                          * Imagine a route A and route B( that depends on A )
    2404             :                          * for recursive resolution and A already exists in the
    2405             :                          * zebra rib.  If zebra receives the routes
    2406             :                          * for resolution at aproximately the same time in the [
    2407             :                          * B, A ] order on the workQ.  If this happens then
    2408             :                          * normal route resolution will happen and B will be
    2409             :                          * resolved successfully and then A will be resolved
    2410             :                          * successfully. Now imagine the reversed order [A, B].
    2411             :                          * A will be resolved and then scheduled for installed
    2412             :                          * (Thus not having the ROUTE_ENTRY_INSTALLED flag ).  B
    2413             :                          * will then get resolved and fail to be installed
    2414             :                          * because the original below test.  Let's `loosen` this
    2415             :                          * up a tiny bit and allow the
    2416             :                          * ROUTE_ENTRY_ROUTE_REPLACING flag ( that is set when a
    2417             :                          * Route Replace operation is being initiated on A now )
    2418             :                          * to now satisfy this situation.  This will allow
    2419             :                          * either order in the workQ to work properly.
    2420             :                          */
    2421           0 :                         if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED) &&
    2422             :                             !CHECK_FLAG(match->status,
    2423             :                                         ROUTE_ENTRY_ROUTE_REPLACING)) {
    2424           0 :                                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2425           0 :                                         zlog_debug(
    2426             :                                                 "%s: match %p (%pNG) not installed or being Route Replaced",
    2427             :                                                 __func__, match, match->nhe);
    2428             : 
    2429           0 :                                 goto done_with_match;
    2430             :                         }
    2431             : 
    2432             :                         /* Examine installed nexthops; note that there
    2433             :                          * may not be any installed primary nexthops if
    2434             :                          * only backups are installed.
    2435             :                          */
    2436           0 :                         nhg = rib_get_fib_nhg(match);
    2437           0 :                         for (ALL_NEXTHOPS_PTR(nhg, newhop)) {
    2438           0 :                                 if (!nexthop_valid_resolve(nexthop, newhop))
    2439           0 :                                         continue;
    2440             : 
    2441           0 :                                 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2442           0 :                                         zlog_debug(
    2443             :                                                 "%s: RECURSIVE match %p (%pNG), newhop %pNHv",
    2444             :                                                 __func__, match, match->nhe,
    2445             :                                                 newhop);
    2446             : 
    2447           0 :                                 SET_FLAG(nexthop->flags,
    2448             :                                          NEXTHOP_FLAG_RECURSIVE);
    2449           0 :                                 resolver = nexthop_set_resolved(afi, newhop,
    2450             :                                                                 nexthop, NULL);
    2451           0 :                                 resolved = 1;
    2452             : 
    2453             :                                 /* If there are backup nexthops, capture
    2454             :                                  * that info with the resolving nexthop.
    2455             :                                  */
    2456           0 :                                 if (resolver && newhop->backup_num > 0) {
    2457           0 :                                         resolve_backup_nexthops(newhop,
    2458           0 :                                                                 match->nhe,
    2459             :                                                                 resolver, nhe,
    2460             :                                                                 &map);
    2461             :                                 }
    2462             :                         }
    2463             : 
    2464             :                         /* Examine installed backup nexthops, if any. There
    2465             :                          * are only installed backups *if* there is a
    2466             :                          * dedicated fib list. The UI can also control use
    2467             :                          * of backups for resolution.
    2468             :                          */
    2469           0 :                         nhg = rib_get_fib_backup_nhg(match);
    2470           0 :                         if (!use_recursive_backups ||
    2471           0 :                             nhg == NULL || nhg->nexthop == NULL)
    2472           0 :                                 goto done_with_match;
    2473             : 
    2474           0 :                         for (ALL_NEXTHOPS_PTR(nhg, newhop)) {
    2475           0 :                                 if (!nexthop_valid_resolve(nexthop, newhop))
    2476           0 :                                         continue;
    2477             : 
    2478           0 :                                 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2479           0 :                                         zlog_debug(
    2480             :                                                 "%s: RECURSIVE match backup %p (%pNG), newhop %pNHv",
    2481             :                                                 __func__, match, match->nhe,
    2482             :                                                 newhop);
    2483             : 
    2484           0 :                                 SET_FLAG(nexthop->flags,
    2485             :                                          NEXTHOP_FLAG_RECURSIVE);
    2486           0 :                                 nexthop_set_resolved(afi, newhop, nexthop,
    2487             :                                                      NULL);
    2488           0 :                                 resolved = 1;
    2489             :                         }
    2490             : 
    2491           0 : done_with_match:
    2492             :                         /* Capture resolving mtu */
    2493           0 :                         if (resolved) {
    2494           0 :                                 if (pmtu)
    2495           0 :                                         *pmtu = match->mtu;
    2496             : 
    2497           0 :                         } else if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2498           0 :                                 zlog_debug(
    2499             :                                         "        %s: Recursion failed to find",
    2500             :                                         __func__);
    2501             : 
    2502           0 :                         return resolved;
    2503             :                 } else {
    2504           0 :                         if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
    2505           0 :                                 zlog_debug(
    2506             :                                         "        %s: Route Type %s has not turned on recursion",
    2507             :                                         __func__, zebra_route_string(type));
    2508           0 :                                 if (type == ZEBRA_ROUTE_BGP
    2509           0 :                                     && !CHECK_FLAG(flags, ZEBRA_FLAG_IBGP))
    2510           0 :                                         zlog_debug(
    2511             :                                                 "        EBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
    2512             :                         }
    2513           0 :                         return 0;
    2514             :                 }
    2515             :         }
    2516           0 :         if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2517           0 :                 zlog_debug("        %s: Nexthop did not lookup in table",
    2518             :                            __func__);
    2519             :         return 0;
    2520             : }
    2521             : 
    2522             : /* This function verifies reachability of one given nexthop, which can be
    2523             :  * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
    2524             :  * in nexthop->flags field. The nexthop->ifindex will be updated
    2525             :  * appropriately as well.
    2526             :  *
    2527             :  * An existing route map can turn an otherwise active nexthop into inactive,
    2528             :  * but not vice versa.
    2529             :  *
    2530             :  * The return value is the final value of 'ACTIVE' flag.
    2531             :  */
    2532          16 : static unsigned nexthop_active_check(struct route_node *rn,
    2533             :                                      struct route_entry *re,
    2534             :                                      struct nexthop *nexthop,
    2535             :                                      struct nhg_hash_entry *nhe)
    2536             : {
    2537          16 :         route_map_result_t ret = RMAP_PERMITMATCH;
    2538          16 :         afi_t family;
    2539          16 :         const struct prefix *p, *src_p;
    2540          16 :         struct zebra_vrf *zvrf;
    2541          16 :         uint32_t mtu = 0;
    2542          16 :         vrf_id_t vrf_id;
    2543             : 
    2544          16 :         srcdest_rnode_prefixes(rn, &p, &src_p);
    2545             : 
    2546          16 :         if (rn->p.family == AF_INET)
    2547             :                 family = AFI_IP;
    2548          10 :         else if (rn->p.family == AF_INET6)
    2549             :                 family = AFI_IP6;
    2550             :         else
    2551           0 :                 family = AF_UNSPEC;
    2552             : 
    2553          16 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2554           0 :                 zlog_debug("%s: re %p, nexthop %pNHv", __func__, re, nexthop);
    2555             : 
    2556             :         /*
    2557             :          * If this is a kernel route, then if the interface is *up* then
    2558             :          * by golly gee whiz it's a good route.
    2559             :          */
    2560          16 :         if (re->type == ZEBRA_ROUTE_KERNEL || re->type == ZEBRA_ROUTE_SYSTEM) {
    2561           0 :                 struct interface *ifp;
    2562             : 
    2563           0 :                 ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
    2564             : 
    2565           0 :                 if (ifp && (if_is_operative(ifp) || if_is_up(ifp))) {
    2566           0 :                         SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2567           0 :                         goto skip_check;
    2568             :                 }
    2569             :         }
    2570             : 
    2571          16 :         vrf_id = zvrf_id(rib_dest_vrf(rib_dest_from_rnode(rn)));
    2572          16 :         switch (nexthop->type) {
    2573          16 :         case NEXTHOP_TYPE_IFINDEX:
    2574          16 :                 if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags,
    2575             :                                    &mtu, vrf_id))
    2576          16 :                         SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2577             :                 else
    2578           0 :                         UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2579             :                 break;
    2580           0 :         case NEXTHOP_TYPE_IPV4:
    2581             :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    2582           0 :                 family = AFI_IP;
    2583           0 :                 if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags,
    2584             :                                    &mtu, vrf_id))
    2585           0 :                         SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2586             :                 else
    2587           0 :                         UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2588             :                 break;
    2589           0 :         case NEXTHOP_TYPE_IPV6:
    2590           0 :                 family = AFI_IP6;
    2591           0 :                 if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags,
    2592             :                                    &mtu, vrf_id))
    2593           0 :                         SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2594             :                 else
    2595           0 :                         UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2596             :                 break;
    2597           0 :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    2598             :                 /* RFC 5549, v4 prefix with v6 NH */
    2599           0 :                 if (rn->p.family != AF_INET)
    2600           0 :                         family = AFI_IP6;
    2601             : 
    2602           0 :                 if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags,
    2603             :                                    &mtu, vrf_id))
    2604           0 :                         SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2605             :                 else
    2606           0 :                         UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2607             :                 break;
    2608           0 :         case NEXTHOP_TYPE_BLACKHOLE:
    2609           0 :                 SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2610           0 :                 break;
    2611             :         default:
    2612             :                 break;
    2613             :         }
    2614             : 
    2615          16 : skip_check:
    2616             : 
    2617          16 :         if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
    2618           0 :                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2619           0 :                         zlog_debug("        %s: Unable to find active nexthop",
    2620             :                                    __func__);
    2621           0 :                 return 0;
    2622             :         }
    2623             : 
    2624             :         /* Capture recursive nexthop mtu.
    2625             :          * TODO -- the code used to just reset the re's value to zero
    2626             :          * for each nexthop, and then jam any resolving route's mtu value in,
    2627             :          * whether or not that was zero, or lt/gt any existing value? The
    2628             :          * way this is used appears to be as a floor value, so let's try
    2629             :          * using it that way here.
    2630             :          */
    2631          16 :         if (mtu > 0) {
    2632           0 :                 if (re->nexthop_mtu == 0 || re->nexthop_mtu > mtu)
    2633           0 :                         re->nexthop_mtu = mtu;
    2634             :         }
    2635             : 
    2636             :         /* XXX: What exactly do those checks do? Do we support
    2637             :          * e.g. IPv4 routes with IPv6 nexthops or vice versa?
    2638             :          */
    2639          16 :         if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET)
    2640           0 :             || (family == AFI_IP6 && p->family != AF_INET6))
    2641             :                 return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2642             : 
    2643             :         /* The original code didn't determine the family correctly
    2644             :          * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
    2645             :          * from the rib_table_info in those cases.
    2646             :          * Possibly it may be better to use only the rib_table_info
    2647             :          * in every case.
    2648             :          */
    2649           0 :         if (family == 0) {
    2650           0 :                 struct rib_table_info *info;
    2651             : 
    2652           0 :                 info = srcdest_rnode_table_info(rn);
    2653           0 :                 family = info->afi;
    2654             :         }
    2655             : 
    2656           0 :         memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
    2657             : 
    2658           0 :         zvrf = zebra_vrf_lookup_by_id(re->vrf_id);
    2659           0 :         if (!zvrf) {
    2660           0 :                 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
    2661           0 :                         zlog_debug("        %s: zvrf is NULL", __func__);
    2662           0 :                 return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2663             :         }
    2664             : 
    2665             :         /* It'll get set if required inside */
    2666           0 :         ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop,
    2667             :                                     zvrf, re->tag);
    2668           0 :         if (ret == RMAP_DENYMATCH) {
    2669           0 :                 if (IS_ZEBRA_DEBUG_RIB) {
    2670           0 :                         zlog_debug(
    2671             :                                 "%u:%pRN: Filtering out with NH %pNHv due to route map",
    2672             :                                 re->vrf_id, rn, nexthop);
    2673             :                 }
    2674           0 :                 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2675             :         }
    2676           0 :         return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2677             : }
    2678             : 
    2679             : /* Helper function called after resolution to walk nhg rb trees
    2680             :  * and toggle the NEXTHOP_GROUP_VALID flag if the nexthop
    2681             :  * is active on singleton NHEs.
    2682             :  */
    2683          16 : static bool zebra_nhg_set_valid_if_active(struct nhg_hash_entry *nhe)
    2684             : {
    2685          16 :         struct nhg_connected *rb_node_dep = NULL;
    2686          16 :         bool valid = false;
    2687             : 
    2688          16 :         if (!zebra_nhg_depends_is_empty(nhe)) {
    2689             :                 /* Is at least one depend valid? */
    2690           0 :                 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
    2691           0 :                         if (zebra_nhg_set_valid_if_active(rb_node_dep->nhe))
    2692           0 :                                 valid = true;
    2693             :                 }
    2694             : 
    2695           0 :                 goto done;
    2696             :         }
    2697             : 
    2698             :         /* should be fully resolved singleton at this point */
    2699          16 :         if (CHECK_FLAG(nhe->nhg.nexthop->flags, NEXTHOP_FLAG_ACTIVE))
    2700             :                 valid = true;
    2701             : 
    2702           0 : done:
    2703           0 :         if (valid)
    2704          16 :                 SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
    2705             : 
    2706          16 :         return valid;
    2707             : }
    2708             : 
    2709             : /*
    2710             :  * Process a list of nexthops, given an nhe, determining
    2711             :  * whether each one is ACTIVE/installable at this time.
    2712             :  */
    2713          16 : static uint32_t nexthop_list_active_update(struct route_node *rn,
    2714             :                                            struct route_entry *re,
    2715             :                                            struct nhg_hash_entry *nhe,
    2716             :                                            bool is_backup)
    2717             : {
    2718          16 :         union g_addr prev_src;
    2719          16 :         unsigned int prev_active, new_active;
    2720          16 :         ifindex_t prev_index;
    2721          16 :         uint32_t counter = 0;
    2722          16 :         struct nexthop *nexthop;
    2723          16 :         struct nexthop_group *nhg = &nhe->nhg;
    2724             : 
    2725          16 :         nexthop = nhg->nexthop;
    2726             : 
    2727             :         /* Init recursive nh mtu */
    2728          16 :         re->nexthop_mtu = 0;
    2729             : 
    2730             :         /* Process nexthops one-by-one */
    2731          32 :         for ( ; nexthop; nexthop = nexthop->next) {
    2732             : 
    2733             :                 /* No protocol daemon provides src and so we're skipping
    2734             :                  * tracking it
    2735             :                  */
    2736          16 :                 prev_src = nexthop->rmap_src;
    2737          16 :                 prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
    2738          16 :                 prev_index = nexthop->ifindex;
    2739             : 
    2740             :                 /* Include the containing nhe for primary nexthops: if there's
    2741             :                  * recursive resolution, we capture the backup info also.
    2742             :                  */
    2743          16 :                 new_active =
    2744          32 :                         nexthop_active_check(rn, re, nexthop,
    2745             :                                              (is_backup ? NULL : nhe));
    2746             : 
    2747             :                 /*
    2748             :                  * We need to respect the multipath_num here
    2749             :                  * as that what we should be able to install from
    2750             :                  * a multipath perspective should not be a data plane
    2751             :                  * decision point.
    2752             :                  */
    2753          16 :                 if (new_active && counter >= zrouter.multipath_num) {
    2754             :                         struct nexthop *nh;
    2755             : 
    2756             :                         /* Set it and its resolved nexthop as inactive. */
    2757           0 :                         for (nh = nexthop; nh; nh = nh->resolved)
    2758           0 :                                 UNSET_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE);
    2759             : 
    2760             :                         new_active = 0;
    2761             :                 }
    2762             : 
    2763          16 :                 if (new_active)
    2764          16 :                         counter++;
    2765             : 
    2766             :                 /* Check for changes to the nexthop - set ROUTE_ENTRY_CHANGED */
    2767          16 :                 if (prev_active != new_active || prev_index != nexthop->ifindex
    2768           0 :                     || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
    2769           0 :                          && nexthop->type < NEXTHOP_TYPE_IPV6)
    2770           0 :                         && prev_src.ipv4.s_addr
    2771           0 :                                    != nexthop->rmap_src.ipv4.s_addr)
    2772           0 :                     || ((nexthop->type >= NEXTHOP_TYPE_IPV6
    2773           0 :                          && nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
    2774           0 :                         && !(IPV6_ADDR_SAME(&prev_src.ipv6,
    2775             :                                             &nexthop->rmap_src.ipv6)))
    2776           0 :                     || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED))
    2777          16 :                         SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
    2778             :         }
    2779             : 
    2780          16 :         return counter;
    2781             : }
    2782             : 
    2783             : 
    2784           0 : static uint32_t proto_nhg_nexthop_active_update(struct nexthop_group *nhg)
    2785             : {
    2786           0 :         struct nexthop *nh;
    2787           0 :         uint32_t curr_active = 0;
    2788             : 
    2789             :         /* Assume all active for now */
    2790             : 
    2791           0 :         for (nh = nhg->nexthop; nh; nh = nh->next) {
    2792           0 :                 SET_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE);
    2793           0 :                 curr_active++;
    2794             :         }
    2795             : 
    2796           0 :         return curr_active;
    2797             : }
    2798             : 
    2799             : /*
    2800             :  * Iterate over all nexthops of the given RIB entry and refresh their
    2801             :  * ACTIVE flag.  If any nexthop is found to toggle the ACTIVE flag,
    2802             :  * the whole re structure is flagged with ROUTE_ENTRY_CHANGED.
    2803             :  *
    2804             :  * Return value is the new number of active nexthops.
    2805             :  */
    2806          16 : int nexthop_active_update(struct route_node *rn, struct route_entry *re)
    2807             : {
    2808          16 :         struct nhg_hash_entry *curr_nhe;
    2809          16 :         uint32_t curr_active = 0, backup_active = 0;
    2810             : 
    2811          16 :         if (PROTO_OWNED(re->nhe))
    2812           0 :                 return proto_nhg_nexthop_active_update(&re->nhe->nhg);
    2813             : 
    2814          16 :         afi_t rt_afi = family2afi(rn->p.family);
    2815             : 
    2816          16 :         UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
    2817             : 
    2818             :         /* Make a local copy of the existing nhe, so we don't work on/modify
    2819             :          * the shared nhe.
    2820             :          */
    2821          16 :         curr_nhe = zebra_nhe_copy(re->nhe, re->nhe->id);
    2822             : 
    2823          16 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2824           0 :                 zlog_debug("%s: re %p nhe %p (%pNG), curr_nhe %p", __func__, re,
    2825             :                            re->nhe, re->nhe, curr_nhe);
    2826             : 
    2827             :         /* Clear the existing id, if any: this will avoid any confusion
    2828             :          * if the id exists, and will also force the creation
    2829             :          * of a new nhe reflecting the changes we may make in this local copy.
    2830             :          */
    2831          16 :         curr_nhe->id = 0;
    2832             : 
    2833             :         /* Process nexthops */
    2834          16 :         curr_active = nexthop_list_active_update(rn, re, curr_nhe, false);
    2835             : 
    2836          16 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2837           0 :                 zlog_debug("%s: re %p curr_active %u", __func__, re,
    2838             :                            curr_active);
    2839             : 
    2840             :         /* If there are no backup nexthops, we are done */
    2841          16 :         if (zebra_nhg_get_backup_nhg(curr_nhe) == NULL)
    2842          16 :                 goto backups_done;
    2843             : 
    2844           0 :         backup_active = nexthop_list_active_update(
    2845             :                 rn, re, curr_nhe->backup_info->nhe, true /*is_backup*/);
    2846             : 
    2847           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2848           0 :                 zlog_debug("%s: re %p backup_active %u", __func__, re,
    2849             :                            backup_active);
    2850             : 
    2851           0 : backups_done:
    2852             : 
    2853             :         /*
    2854             :          * Ref or create an nhe that matches the current state of the
    2855             :          * nexthop(s).
    2856             :          */
    2857          16 :         if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) {
    2858          16 :                 struct nhg_hash_entry *new_nhe = NULL;
    2859             : 
    2860          16 :                 new_nhe = zebra_nhg_rib_find_nhe(curr_nhe, rt_afi);
    2861             : 
    2862          16 :                 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2863           0 :                         zlog_debug(
    2864             :                                 "%s: re %p CHANGED: nhe %p (%pNG) => new_nhe %p (%pNG)",
    2865             :                                 __func__, re, re->nhe, re->nhe, new_nhe,
    2866             :                                 new_nhe);
    2867             : 
    2868          16 :                 route_entry_update_nhe(re, new_nhe);
    2869             :         }
    2870             : 
    2871             : 
    2872             :         /* Walk the NHE depends tree and toggle NEXTHOP_GROUP_VALID
    2873             :          * flag where appropriate.
    2874             :          */
    2875          16 :         if (curr_active)
    2876          16 :                 zebra_nhg_set_valid_if_active(re->nhe);
    2877             : 
    2878             :         /*
    2879             :          * Do not need the old / copied nhe anymore since it
    2880             :          * was either copied over into a new nhe or not
    2881             :          * used at all.
    2882             :          */
    2883          16 :         zebra_nhg_free(curr_nhe);
    2884          16 :         return curr_active;
    2885             : }
    2886             : 
    2887             : /* Recursively construct a grp array of fully resolved IDs.
    2888             :  *
    2889             :  * This function allows us to account for groups within groups,
    2890             :  * by converting them into a flat array of IDs.
    2891             :  *
    2892             :  * nh_grp is modified at every level of recursion to append
    2893             :  * to it the next unique, fully resolved ID from the entire tree.
    2894             :  *
    2895             :  *
    2896             :  * Note:
    2897             :  * I'm pretty sure we only allow ONE level of group within group currently.
    2898             :  * But making this recursive just in case that ever changes.
    2899             :  */
    2900           0 : static uint8_t zebra_nhg_nhe2grp_internal(struct nh_grp *grp,
    2901             :                                           uint8_t curr_index,
    2902             :                                           struct nhg_hash_entry *nhe,
    2903             :                                           int max_num)
    2904             : {
    2905           0 :         struct nhg_connected *rb_node_dep = NULL;
    2906           0 :         struct nhg_hash_entry *depend = NULL;
    2907           0 :         uint8_t i = curr_index;
    2908             : 
    2909           0 :         frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
    2910           0 :                 bool duplicate = false;
    2911             : 
    2912           0 :                 if (i >= max_num)
    2913           0 :                         goto done;
    2914             : 
    2915           0 :                 depend = rb_node_dep->nhe;
    2916             : 
    2917             :                 /*
    2918             :                  * If its recursive, use its resolved nhe in the group
    2919             :                  */
    2920           0 :                 if (CHECK_FLAG(depend->flags, NEXTHOP_GROUP_RECURSIVE)) {
    2921           0 :                         depend = zebra_nhg_resolve(depend);
    2922           0 :                         if (!depend) {
    2923           0 :                                 flog_err(
    2924             :                                         EC_ZEBRA_NHG_FIB_UPDATE,
    2925             :                                         "Failed to recursively resolve Nexthop Hash Entry in the group id=%pNG",
    2926             :                                         nhe);
    2927           0 :                                 continue;
    2928             :                         }
    2929             :                 }
    2930             : 
    2931           0 :                 if (!zebra_nhg_depends_is_empty(depend)) {
    2932             :                         /* This is a group within a group */
    2933           0 :                         i = zebra_nhg_nhe2grp_internal(grp, i, depend, max_num);
    2934             :                 } else {
    2935           0 :                         if (!CHECK_FLAG(depend->flags, NEXTHOP_GROUP_VALID)) {
    2936           0 :                                 if (IS_ZEBRA_DEBUG_RIB_DETAILED
    2937           0 :                                     || IS_ZEBRA_DEBUG_NHG)
    2938           0 :                                         zlog_debug(
    2939             :                                                 "%s: Nexthop ID (%u) not valid, not appending to dataplane install group",
    2940             :                                                 __func__, depend->id);
    2941           0 :                                 continue;
    2942             :                         }
    2943             : 
    2944             :                         /* If the nexthop not installed/queued for install don't
    2945             :                          * put in the ID array.
    2946             :                          */
    2947           0 :                         if (!(CHECK_FLAG(depend->flags, NEXTHOP_GROUP_INSTALLED)
    2948             :                               || CHECK_FLAG(depend->flags,
    2949             :                                             NEXTHOP_GROUP_QUEUED))) {
    2950           0 :                                 if (IS_ZEBRA_DEBUG_RIB_DETAILED
    2951           0 :                                     || IS_ZEBRA_DEBUG_NHG)
    2952           0 :                                         zlog_debug(
    2953             :                                                 "%s: Nexthop ID (%u) not installed or queued for install, not appending to dataplane install group",
    2954             :                                                 __func__, depend->id);
    2955           0 :                                 continue;
    2956             :                         }
    2957             : 
    2958             :                         /* Check for duplicate IDs, ignore if found. */
    2959           0 :                         for (int j = 0; j < i; j++) {
    2960           0 :                                 if (depend->id == grp[j].id) {
    2961             :                                         duplicate = true;
    2962             :                                         break;
    2963             :                                 }
    2964             :                         }
    2965             : 
    2966           0 :                         if (duplicate) {
    2967           0 :                                 if (IS_ZEBRA_DEBUG_RIB_DETAILED
    2968           0 :                                     || IS_ZEBRA_DEBUG_NHG)
    2969           0 :                                         zlog_debug(
    2970             :                                                 "%s: Nexthop ID (%u) is duplicate, not appending to dataplane install group",
    2971             :                                                 __func__, depend->id);
    2972           0 :                                 continue;
    2973             :                         }
    2974             : 
    2975           0 :                         grp[i].id = depend->id;
    2976           0 :                         grp[i].weight = depend->nhg.nexthop->weight;
    2977           0 :                         i++;
    2978             :                 }
    2979             :         }
    2980             : 
    2981           0 :         if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL)
    2982           0 :                 goto done;
    2983             : 
    2984             :         /* TODO -- For now, we are not trying to use or install any
    2985             :          * backup info in this nexthop-id path: we aren't prepared
    2986             :          * to use the backups here yet. We're just debugging what we find.
    2987             :          */
    2988           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    2989           0 :                 zlog_debug("%s: skipping backup nhe",  __func__);
    2990             : 
    2991           0 : done:
    2992           0 :         return i;
    2993             : }
    2994             : 
    2995             : /* Convert a nhe into a group array */
    2996           0 : uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
    2997             :                           int max_num)
    2998             : {
    2999             :         /* Call into the recursive function */
    3000           0 :         return zebra_nhg_nhe2grp_internal(grp, 0, nhe, max_num);
    3001             : }
    3002             : 
    3003           8 : void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe)
    3004             : {
    3005           8 :         struct nhg_connected *rb_node_dep = NULL;
    3006             : 
    3007             :         /* Resolve it first */
    3008           8 :         nhe = zebra_nhg_resolve(nhe);
    3009             : 
    3010             :         /* Make sure all depends are installed/queued */
    3011          16 :         frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
    3012           0 :                 zebra_nhg_install_kernel(rb_node_dep->nhe);
    3013             :         }
    3014             : 
    3015           8 :         if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)
    3016             :             && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)
    3017           8 :             && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) {
    3018             :                 /* Change its type to us since we are installing it */
    3019           6 :                 if (!ZEBRA_NHG_CREATED(nhe))
    3020           0 :                         nhe->type = ZEBRA_ROUTE_NHG;
    3021             : 
    3022           6 :                 int ret = dplane_nexthop_add(nhe);
    3023             : 
    3024           6 :                 switch (ret) {
    3025           6 :                 case ZEBRA_DPLANE_REQUEST_QUEUED:
    3026           6 :                         SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
    3027           6 :                         break;
    3028           0 :                 case ZEBRA_DPLANE_REQUEST_FAILURE:
    3029           0 :                         flog_err(
    3030             :                                 EC_ZEBRA_DP_INSTALL_FAIL,
    3031             :                                 "Failed to install Nexthop ID (%pNG) into the kernel",
    3032             :                                 nhe);
    3033           0 :                         break;
    3034           0 :                 case ZEBRA_DPLANE_REQUEST_SUCCESS:
    3035           0 :                         SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
    3036           0 :                         zebra_nhg_handle_install(nhe);
    3037           0 :                         break;
    3038             :                 }
    3039             :         }
    3040           8 : }
    3041             : 
    3042          13 : void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe)
    3043             : {
    3044          13 :         if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) {
    3045           4 :                 int ret = dplane_nexthop_delete(nhe);
    3046             : 
    3047           4 :                 switch (ret) {
    3048           4 :                 case ZEBRA_DPLANE_REQUEST_QUEUED:
    3049           4 :                         SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
    3050           4 :                         break;
    3051           0 :                 case ZEBRA_DPLANE_REQUEST_FAILURE:
    3052           0 :                         flog_err(
    3053             :                                 EC_ZEBRA_DP_DELETE_FAIL,
    3054             :                                 "Failed to uninstall Nexthop ID (%pNG) from the kernel",
    3055             :                                 nhe);
    3056           0 :                         break;
    3057           0 :                 case ZEBRA_DPLANE_REQUEST_SUCCESS:
    3058           0 :                         UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
    3059           0 :                         break;
    3060             :                 }
    3061             :         }
    3062             : 
    3063          13 :         zebra_nhg_handle_uninstall(nhe);
    3064          13 : }
    3065             : 
    3066           6 : void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
    3067             : {
    3068           6 :         enum dplane_op_e op;
    3069           6 :         enum zebra_dplane_result status;
    3070           6 :         uint32_t id = 0;
    3071           6 :         struct nhg_hash_entry *nhe = NULL;
    3072             : 
    3073           6 :         op = dplane_ctx_get_op(ctx);
    3074           6 :         status = dplane_ctx_get_status(ctx);
    3075             : 
    3076           6 :         id = dplane_ctx_get_nhe_id(ctx);
    3077             : 
    3078           6 :         if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_NHG_DETAIL)
    3079           0 :                 zlog_debug(
    3080             :                         "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s",
    3081             :                         ctx, dplane_op2str(op), id, dplane_res2str(status));
    3082             : 
    3083           6 :         switch (op) {
    3084           0 :         case DPLANE_OP_NH_DELETE:
    3085           0 :                 if (status != ZEBRA_DPLANE_REQUEST_SUCCESS)
    3086           0 :                         flog_err(
    3087             :                                 EC_ZEBRA_DP_DELETE_FAIL,
    3088             :                                 "Failed to uninstall Nexthop ID (%u) from the kernel",
    3089             :                                 id);
    3090             : 
    3091             :                 /* We already free'd the data, nothing to do */
    3092             :                 break;
    3093           6 :         case DPLANE_OP_NH_INSTALL:
    3094             :         case DPLANE_OP_NH_UPDATE:
    3095           6 :                 nhe = zebra_nhg_lookup_id(id);
    3096             : 
    3097           6 :                 if (!nhe) {
    3098           0 :                         if (IS_ZEBRA_DEBUG_NHG)
    3099           0 :                                 zlog_debug(
    3100             :                                         "%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table",
    3101             :                                         dplane_op2str(op), id);
    3102             : 
    3103             :                         break;
    3104             :                 }
    3105             : 
    3106           6 :                 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
    3107           6 :                 if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
    3108           4 :                         SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
    3109           4 :                         SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
    3110           4 :                         zebra_nhg_handle_install(nhe);
    3111             : 
    3112             :                         /* If daemon nhg, send it an update */
    3113           4 :                         if (PROTO_OWNED(nhe))
    3114           0 :                                 zsend_nhg_notify(nhe->type, nhe->zapi_instance,
    3115             :                                                  nhe->zapi_session, nhe->id,
    3116             :                                                  ZAPI_NHG_INSTALLED);
    3117             :                 } else {
    3118             :                         /* If daemon nhg, send it an update */
    3119           2 :                         if (PROTO_OWNED(nhe))
    3120           0 :                                 zsend_nhg_notify(nhe->type, nhe->zapi_instance,
    3121             :                                                  nhe->zapi_session, nhe->id,
    3122             :                                                  ZAPI_NHG_FAIL_INSTALL);
    3123             : 
    3124           2 :                         if (!(zebra_nhg_proto_nexthops_only() &&
    3125           0 :                               !PROTO_OWNED(nhe)))
    3126           2 :                                 flog_err(
    3127             :                                         EC_ZEBRA_DP_INSTALL_FAIL,
    3128             :                                         "Failed to install Nexthop (%pNG) into the kernel",
    3129             :                                         nhe);
    3130             :                 }
    3131             :                 break;
    3132             : 
    3133             :         case DPLANE_OP_ROUTE_INSTALL:
    3134             :         case DPLANE_OP_ROUTE_UPDATE:
    3135             :         case DPLANE_OP_ROUTE_DELETE:
    3136             :         case DPLANE_OP_ROUTE_NOTIFY:
    3137             :         case DPLANE_OP_LSP_INSTALL:
    3138             :         case DPLANE_OP_LSP_UPDATE:
    3139             :         case DPLANE_OP_LSP_DELETE:
    3140             :         case DPLANE_OP_LSP_NOTIFY:
    3141             :         case DPLANE_OP_PW_INSTALL:
    3142             :         case DPLANE_OP_PW_UNINSTALL:
    3143             :         case DPLANE_OP_SYS_ROUTE_ADD:
    3144             :         case DPLANE_OP_SYS_ROUTE_DELETE:
    3145             :         case DPLANE_OP_ADDR_INSTALL:
    3146             :         case DPLANE_OP_ADDR_UNINSTALL:
    3147             :         case DPLANE_OP_MAC_INSTALL:
    3148             :         case DPLANE_OP_MAC_DELETE:
    3149             :         case DPLANE_OP_NEIGH_INSTALL:
    3150             :         case DPLANE_OP_NEIGH_UPDATE:
    3151             :         case DPLANE_OP_NEIGH_DELETE:
    3152             :         case DPLANE_OP_NEIGH_IP_INSTALL:
    3153             :         case DPLANE_OP_NEIGH_IP_DELETE:
    3154             :         case DPLANE_OP_VTEP_ADD:
    3155             :         case DPLANE_OP_VTEP_DELETE:
    3156             :         case DPLANE_OP_RULE_ADD:
    3157             :         case DPLANE_OP_RULE_DELETE:
    3158             :         case DPLANE_OP_RULE_UPDATE:
    3159             :         case DPLANE_OP_NEIGH_DISCOVER:
    3160             :         case DPLANE_OP_BR_PORT_UPDATE:
    3161             :         case DPLANE_OP_NONE:
    3162             :         case DPLANE_OP_IPTABLE_ADD:
    3163             :         case DPLANE_OP_IPTABLE_DELETE:
    3164             :         case DPLANE_OP_IPSET_ADD:
    3165             :         case DPLANE_OP_IPSET_DELETE:
    3166             :         case DPLANE_OP_IPSET_ENTRY_ADD:
    3167             :         case DPLANE_OP_IPSET_ENTRY_DELETE:
    3168             :         case DPLANE_OP_NEIGH_TABLE_UPDATE:
    3169             :         case DPLANE_OP_GRE_SET:
    3170             :         case DPLANE_OP_INTF_ADDR_ADD:
    3171             :         case DPLANE_OP_INTF_ADDR_DEL:
    3172             :         case DPLANE_OP_INTF_NETCONFIG:
    3173             :         case DPLANE_OP_INTF_INSTALL:
    3174             :         case DPLANE_OP_INTF_UPDATE:
    3175             :         case DPLANE_OP_INTF_DELETE:
    3176             :         case DPLANE_OP_TC_QDISC_INSTALL:
    3177             :         case DPLANE_OP_TC_QDISC_UNINSTALL:
    3178             :         case DPLANE_OP_TC_CLASS_ADD:
    3179             :         case DPLANE_OP_TC_CLASS_DELETE:
    3180             :         case DPLANE_OP_TC_CLASS_UPDATE:
    3181             :         case DPLANE_OP_TC_FILTER_ADD:
    3182             :         case DPLANE_OP_TC_FILTER_DELETE:
    3183             :         case DPLANE_OP_TC_FILTER_UPDATE:
    3184             :                 break;
    3185             :         }
    3186           6 : }
    3187             : 
    3188           0 : static int zebra_nhg_sweep_entry(struct hash_bucket *bucket, void *arg)
    3189             : {
    3190           0 :         struct nhg_hash_entry *nhe = NULL;
    3191             : 
    3192           0 :         nhe = (struct nhg_hash_entry *)bucket->data;
    3193             : 
    3194             :         /*
    3195             :          * same logic as with routes.
    3196             :          *
    3197             :          * If older than startup time, we know we read them in from the
    3198             :          * kernel and have not gotten and update for them since startup
    3199             :          * from an upper level proto.
    3200             :          */
    3201           0 :         if (zrouter.startup_time < nhe->uptime)
    3202             :                 return HASHWALK_CONTINUE;
    3203             : 
    3204             :         /*
    3205             :          * If it's proto-owned and not being used by a route, remove it since
    3206             :          * we haven't gotten an update about it from the proto since startup.
    3207             :          * This means that either the config for it was removed or the daemon
    3208             :          * didn't get started. This handles graceful restart & retain scenario.
    3209             :          */
    3210           0 :         if (PROTO_OWNED(nhe) && nhe->refcnt == 1) {
    3211           0 :                 zebra_nhg_decrement_ref(nhe);
    3212           0 :                 return HASHWALK_ABORT;
    3213             :         }
    3214             : 
    3215             :         /*
    3216             :          * If its being ref'd by routes, just let it be uninstalled via a route
    3217             :          * removal.
    3218             :          */
    3219           0 :         if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0) {
    3220           0 :                 zebra_nhg_uninstall_kernel(nhe);
    3221           0 :                 return HASHWALK_ABORT;
    3222             :         }
    3223             : 
    3224             :         return HASHWALK_CONTINUE;
    3225             : }
    3226             : 
    3227           1 : void zebra_nhg_sweep_table(struct hash *hash)
    3228             : {
    3229           1 :         uint32_t count;
    3230             : 
    3231             :         /*
    3232             :          * Yes this is extremely odd.  Effectively nhg's have
    3233             :          * other nexthop groups that depend on them and when you
    3234             :          * remove them, you can have other entries blown up.
    3235             :          * our hash code does not work with deleting multiple
    3236             :          * entries at a time and will possibly cause crashes
    3237             :          * So what to do?  Whenever zebra_nhg_sweep_entry
    3238             :          * deletes an entry it will return HASHWALK_ABORT,
    3239             :          * cause that deletion might have triggered more.
    3240             :          * then we can just keep sweeping this table
    3241             :          * until nothing more is found to do.
    3242             :          */
    3243           1 :         do {
    3244           1 :                 count = hashcount(hash);
    3245           1 :                 hash_walk(hash, zebra_nhg_sweep_entry, NULL);
    3246           1 :         } while (count != hashcount(hash));
    3247           1 : }
    3248             : 
    3249           0 : static void zebra_nhg_mark_keep_entry(struct hash_bucket *bucket, void *arg)
    3250             : {
    3251           0 :         struct nhg_hash_entry *nhe = bucket->data;
    3252             : 
    3253           0 :         UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
    3254           0 : }
    3255             : 
    3256             : /*
    3257             :  * When we are shutting down and we have retain mode enabled
    3258             :  * in zebra the process is to mark each vrf that it's
    3259             :  * routes should not be deleted.  The problem with that
    3260             :  * is that shutdown actually free's up memory which
    3261             :  * causes the nexthop group's ref counts to go to zero
    3262             :  * we need a way to subtly tell the system to not remove
    3263             :  * the nexthop groups from the kernel at the same time.
    3264             :  * The easiest just looks like that we should not mark
    3265             :  * the nhg's as installed any more and when the ref count
    3266             :  * goes to zero we'll attempt to delete and do nothing
    3267             :  */
    3268           0 : void zebra_nhg_mark_keep(void)
    3269             : {
    3270           0 :         hash_iterate(zrouter.nhgs_id, zebra_nhg_mark_keep_entry, NULL);
    3271           0 : }
    3272             : 
    3273             : /* Global control to disable use of kernel nexthops, if available. We can't
    3274             :  * force the kernel to support nexthop ids, of course, but we can disable
    3275             :  * zebra's use of them, for testing e.g. By default, if the kernel supports
    3276             :  * nexthop ids, zebra uses them.
    3277             :  */
    3278           0 : void zebra_nhg_enable_kernel_nexthops(bool set)
    3279             : {
    3280           0 :         g_nexthops_enabled = set;
    3281           0 : }
    3282             : 
    3283          35 : bool zebra_nhg_kernel_nexthops_enabled(void)
    3284             : {
    3285          35 :         return g_nexthops_enabled;
    3286             : }
    3287             : 
    3288             : /* Global control for use of activated backups for recursive resolution. */
    3289           0 : void zebra_nhg_set_recursive_use_backups(bool set)
    3290             : {
    3291           0 :         use_recursive_backups = set;
    3292           0 : }
    3293             : 
    3294           0 : bool zebra_nhg_recursive_use_backups(void)
    3295             : {
    3296           0 :         return use_recursive_backups;
    3297             : }
    3298             : 
    3299             : /*
    3300             :  * Global control to only use kernel nexthops for protocol created NHGs.
    3301             :  * There are some use cases where you may not want zebra to implicitly
    3302             :  * create kernel nexthops for all routes and only create them for NHGs
    3303             :  * passed down by upper level protos.
    3304             :  *
    3305             :  * Default is off.
    3306             :  */
    3307           0 : void zebra_nhg_set_proto_nexthops_only(bool set)
    3308             : {
    3309           0 :         proto_nexthops_only = set;
    3310           0 : }
    3311             : 
    3312          12 : bool zebra_nhg_proto_nexthops_only(void)
    3313             : {
    3314           2 :         return proto_nexthops_only;
    3315             : }
    3316             : 
    3317             : /* Add NHE from upper level proto */
    3318           0 : struct nhg_hash_entry *zebra_nhg_proto_add(uint32_t id, int type,
    3319             :                                            uint16_t instance, uint32_t session,
    3320             :                                            struct nexthop_group *nhg, afi_t afi)
    3321             : {
    3322           0 :         struct nhg_hash_entry lookup;
    3323           0 :         struct nhg_hash_entry *new, *old;
    3324           0 :         struct nhg_connected *rb_node_dep = NULL;
    3325           0 :         struct nexthop *newhop;
    3326           0 :         bool replace = false;
    3327             : 
    3328           0 :         if (!nhg->nexthop) {
    3329           0 :                 if (IS_ZEBRA_DEBUG_NHG)
    3330           0 :                         zlog_debug("%s: id %u, no nexthops passed to add",
    3331             :                                    __func__, id);
    3332           0 :                 return NULL;
    3333             :         }
    3334             : 
    3335             : 
    3336             :         /* Set nexthop list as active, since they wont go through rib
    3337             :          * processing.
    3338             :          *
    3339             :          * Assuming valid/onlink for now.
    3340             :          *
    3341             :          * Once resolution is figured out, we won't need this!
    3342             :          */
    3343           0 :         for (ALL_NEXTHOPS_PTR(nhg, newhop)) {
    3344           0 :                 if (CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
    3345           0 :                         if (IS_ZEBRA_DEBUG_NHG)
    3346           0 :                                 zlog_debug(
    3347             :                                         "%s: id %u, backup nexthops not supported",
    3348             :                                         __func__, id);
    3349           0 :                         return NULL;
    3350             :                 }
    3351             : 
    3352           0 :                 if (newhop->type == NEXTHOP_TYPE_BLACKHOLE) {
    3353           0 :                         if (IS_ZEBRA_DEBUG_NHG)
    3354           0 :                                 zlog_debug(
    3355             :                                         "%s: id %u, blackhole nexthop not supported",
    3356             :                                         __func__, id);
    3357           0 :                         return NULL;
    3358             :                 }
    3359             : 
    3360           0 :                 if (newhop->type == NEXTHOP_TYPE_IFINDEX) {
    3361           0 :                         if (IS_ZEBRA_DEBUG_NHG)
    3362           0 :                                 zlog_debug(
    3363             :                                         "%s: id %u, nexthop without gateway not supported",
    3364             :                                         __func__, id);
    3365           0 :                         return NULL;
    3366             :                 }
    3367             : 
    3368           0 :                 if (!newhop->ifindex) {
    3369           0 :                         if (IS_ZEBRA_DEBUG_NHG)
    3370           0 :                                 zlog_debug(
    3371             :                                         "%s: id %u, nexthop without ifindex is not supported",
    3372             :                                         __func__, id);
    3373           0 :                         return NULL;
    3374             :                 }
    3375           0 :                 SET_FLAG(newhop->flags, NEXTHOP_FLAG_ACTIVE);
    3376             :         }
    3377             : 
    3378           0 :         zebra_nhe_init(&lookup, afi, nhg->nexthop);
    3379           0 :         lookup.nhg.nexthop = nhg->nexthop;
    3380           0 :         lookup.nhg.nhgr = nhg->nhgr;
    3381           0 :         lookup.id = id;
    3382           0 :         lookup.type = type;
    3383             : 
    3384           0 :         old = zebra_nhg_lookup_id(id);
    3385             : 
    3386           0 :         if (old) {
    3387             :                 /*
    3388             :                  * This is a replace, just release NHE from ID for now, The
    3389             :                  * depends/dependents may still be used in the replacement so
    3390             :                  * we don't touch them other than to remove their refs to their
    3391             :                  * old parent.
    3392             :                  */
    3393           0 :                 replace = true;
    3394           0 :                 hash_release(zrouter.nhgs_id, old);
    3395             : 
    3396             :                 /* Free all the things */
    3397           0 :                 zebra_nhg_release_all_deps(old);
    3398             :         }
    3399             : 
    3400           0 :         new = zebra_nhg_rib_find_nhe(&lookup, afi);
    3401             : 
    3402           0 :         zebra_nhg_increment_ref(new);
    3403             : 
    3404             :         /* Capture zapi client info */
    3405           0 :         new->zapi_instance = instance;
    3406           0 :         new->zapi_session = session;
    3407             : 
    3408           0 :         zebra_nhg_set_valid_if_active(new);
    3409             : 
    3410           0 :         zebra_nhg_install_kernel(new);
    3411             : 
    3412           0 :         if (old) {
    3413             :                 /*
    3414             :                  * Check to handle recving DEL while routes still in use then
    3415             :                  * a replace.
    3416             :                  *
    3417             :                  * In this case we would have decremented the refcnt already
    3418             :                  * but set the FLAG here. Go ahead and increment once to fix
    3419             :                  * the misordering we have been sent.
    3420             :                  */
    3421           0 :                 if (CHECK_FLAG(old->flags, NEXTHOP_GROUP_PROTO_RELEASED))
    3422           0 :                         zebra_nhg_increment_ref(old);
    3423             : 
    3424           0 :                 rib_handle_nhg_replace(old, new);
    3425             : 
    3426             :                 /* We have to decrement its singletons
    3427             :                  * because some might not exist in NEW.
    3428             :                  */
    3429           0 :                 if (!zebra_nhg_depends_is_empty(old)) {
    3430           0 :                         frr_each (nhg_connected_tree, &old->nhg_depends,
    3431             :                                   rb_node_dep)
    3432           0 :                                 zebra_nhg_decrement_ref(rb_node_dep->nhe);
    3433             :                 }
    3434             : 
    3435             :                 /* Dont call the dec API, we dont want to uninstall the ID */
    3436           0 :                 old->refcnt = 0;
    3437           0 :                 THREAD_OFF(old->timer);
    3438           0 :                 zebra_nhg_free(old);
    3439           0 :                 old = NULL;
    3440             :         }
    3441             : 
    3442           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    3443           0 :                 zlog_debug("%s: %s nhe %p (%u), vrf %d, type %s", __func__,
    3444             :                            (replace ? "replaced" : "added"), new, new->id,
    3445             :                            new->vrf_id, zebra_route_string(new->type));
    3446             : 
    3447             :         return new;
    3448             : }
    3449             : 
    3450             : /* Delete NHE from upper level proto, caller must decrement ref */
    3451           0 : struct nhg_hash_entry *zebra_nhg_proto_del(uint32_t id, int type)
    3452             : {
    3453           0 :         struct nhg_hash_entry *nhe;
    3454             : 
    3455           0 :         nhe = zebra_nhg_lookup_id(id);
    3456             : 
    3457           0 :         if (!nhe) {
    3458           0 :                 if (IS_ZEBRA_DEBUG_NHG)
    3459           0 :                         zlog_debug("%s: id %u, lookup failed", __func__, id);
    3460             : 
    3461           0 :                 return NULL;
    3462             :         }
    3463             : 
    3464           0 :         if (type != nhe->type) {
    3465           0 :                 if (IS_ZEBRA_DEBUG_NHG)
    3466           0 :                         zlog_debug(
    3467             :                                 "%s: id %u, type %s mismatch, sent by %s, ignoring",
    3468             :                                 __func__, id, zebra_route_string(nhe->type),
    3469             :                                 zebra_route_string(type));
    3470           0 :                 return NULL;
    3471             :         }
    3472             : 
    3473           0 :         if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_PROTO_RELEASED)) {
    3474           0 :                 if (IS_ZEBRA_DEBUG_NHG)
    3475           0 :                         zlog_debug("%s: id %u, already released", __func__, id);
    3476             : 
    3477           0 :                 return NULL;
    3478             :         }
    3479             : 
    3480           0 :         SET_FLAG(nhe->flags, NEXTHOP_GROUP_PROTO_RELEASED);
    3481             : 
    3482           0 :         if (nhe->refcnt > 1) {
    3483           0 :                 if (IS_ZEBRA_DEBUG_NHG)
    3484           0 :                         zlog_debug(
    3485             :                                 "%s: %pNG, still being used by routes refcnt %u",
    3486             :                                 __func__, nhe, nhe->refcnt);
    3487           0 :                 return nhe;
    3488             :         }
    3489             : 
    3490           0 :         if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    3491           0 :                 zlog_debug("%s: deleted nhe %p (%pNG), vrf %d, type %s",
    3492             :                            __func__, nhe, nhe, nhe->vrf_id,
    3493             :                            zebra_route_string(nhe->type));
    3494             : 
    3495             :         return nhe;
    3496             : }
    3497             : 
    3498             : struct nhg_score_proto_iter {
    3499             :         int type;
    3500             :         struct list *found;
    3501             : };
    3502             : 
    3503          18 : static void zebra_nhg_score_proto_entry(struct hash_bucket *bucket, void *arg)
    3504             : {
    3505          18 :         struct nhg_hash_entry *nhe;
    3506          18 :         struct nhg_score_proto_iter *iter;
    3507             : 
    3508          18 :         nhe = (struct nhg_hash_entry *)bucket->data;
    3509          18 :         iter = arg;
    3510             : 
    3511             :         /* Needs to match type and outside zebra ID space */
    3512          18 :         if (nhe->type == iter->type && PROTO_OWNED(nhe)) {
    3513           0 :                 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
    3514           0 :                         zlog_debug(
    3515             :                                 "%s: found nhe %p (%pNG), vrf %d, type %s after client disconnect",
    3516             :                                 __func__, nhe, nhe, nhe->vrf_id,
    3517             :                                 zebra_route_string(nhe->type));
    3518             : 
    3519             :                 /* Add to removal list */
    3520           0 :                 listnode_add(iter->found, nhe);
    3521             :         }
    3522          18 : }
    3523             : 
    3524             : /* Remove specific by proto NHGs */
    3525           3 : unsigned long zebra_nhg_score_proto(int type)
    3526             : {
    3527           3 :         struct nhg_hash_entry *nhe;
    3528           3 :         struct nhg_score_proto_iter iter = {};
    3529           3 :         struct listnode *ln;
    3530           3 :         unsigned long count;
    3531             : 
    3532           3 :         iter.type = type;
    3533           3 :         iter.found = list_new();
    3534             : 
    3535             :         /* Find matching entries to remove */
    3536           3 :         hash_iterate(zrouter.nhgs_id, zebra_nhg_score_proto_entry, &iter);
    3537             : 
    3538             :         /* Now remove them */
    3539           6 :         for (ALL_LIST_ELEMENTS_RO(iter.found, ln, nhe)) {
    3540             :                 /*
    3541             :                  * This should be the last ref if we remove client routes too,
    3542             :                  * and thus should remove and free them.
    3543             :                  */
    3544           0 :                 zebra_nhg_decrement_ref(nhe);
    3545             :         }
    3546             : 
    3547           3 :         count = iter.found->count;
    3548           3 :         list_delete(&iter.found);
    3549             : 
    3550           3 :         return count;
    3551             : }
    3552             : 
    3553           1 : printfrr_ext_autoreg_p("NG", printfrr_nhghe);
    3554           2 : static ssize_t printfrr_nhghe(struct fbuf *buf, struct printfrr_eargs *ea,
    3555             :                               const void *ptr)
    3556             : {
    3557           2 :         const struct nhg_hash_entry *nhe = ptr;
    3558           2 :         const struct nhg_connected *dep;
    3559           2 :         ssize_t ret = 0;
    3560             : 
    3561           2 :         if (!nhe)
    3562           0 :                 return bputs(buf, "[NULL]");
    3563             : 
    3564           2 :         ret += bprintfrr(buf, "%u[", nhe->id);
    3565           2 :         if (nhe->ifp)
    3566           2 :                 ret += printfrr_nhs(buf, nhe->nhg.nexthop);
    3567             :         else {
    3568           0 :                 int count = zebra_nhg_depends_count(nhe);
    3569             : 
    3570           0 :                 frr_each (nhg_connected_tree_const, &nhe->nhg_depends, dep) {
    3571           0 :                         ret += bprintfrr(buf, "%u", dep->nhe->id);
    3572           0 :                         if (count > 1)
    3573           0 :                                 ret += bputs(buf, "/");
    3574           0 :                         count--;
    3575             :                 }
    3576             :         }
    3577             : 
    3578           2 :         ret += bputs(buf, "]");
    3579           2 :         return ret;
    3580             : }

Generated by: LCOV version v1.16-topotato