back to topotato report
topotato coverage report
Current view: top level - zebra - zebra_rnh.c (source / functions) Hit Total Coverage
Test: test_mld_basic.py::MLDBasic Lines: 309 833 37.1 %
Date: 2023-02-24 18:38:01 Functions: 26 41 63.4 %

          Line data    Source code
       1             : /* Zebra next hop tracking code
       2             :  * Copyright (C) 2013 Cumulus Networks, Inc.
       3             :  *
       4             :  * This file is part of GNU Zebra.
       5             :  *
       6             :  * GNU Zebra is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by the
       8             :  * Free Software Foundation; either version 2, or (at your option) any
       9             :  * later version.
      10             :  *
      11             :  * GNU Zebra is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License along
      17             :  * with this program; see the file COPYING; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : #include <zebra.h>
      22             : 
      23             : #include "prefix.h"
      24             : #include "table.h"
      25             : #include "memory.h"
      26             : #include "command.h"
      27             : #include "if.h"
      28             : #include "log.h"
      29             : #include "sockunion.h"
      30             : #include "linklist.h"
      31             : #include "thread.h"
      32             : #include "workqueue.h"
      33             : #include "prefix.h"
      34             : #include "routemap.h"
      35             : #include "stream.h"
      36             : #include "nexthop.h"
      37             : #include "vrf.h"
      38             : 
      39             : #include "zebra/zebra_router.h"
      40             : #include "zebra/rib.h"
      41             : #include "zebra/rt.h"
      42             : #include "zebra/zserv.h"
      43             : #include "zebra/zebra_ns.h"
      44             : #include "zebra/zebra_vrf.h"
      45             : #include "zebra/redistribute.h"
      46             : #include "zebra/debug.h"
      47             : #include "zebra/zebra_rnh.h"
      48             : #include "zebra/zebra_routemap.h"
      49             : #include "zebra/zebra_srte.h"
      50             : #include "zebra/interface.h"
      51             : #include "zebra/zebra_errors.h"
      52             : 
      53           3 : DEFINE_MTYPE_STATIC(ZEBRA, RNH, "Nexthop tracking object");
      54             : 
      55             : /* UI controls whether to notify about changes that only involve backup
      56             :  * nexthops. Default is to notify all changes.
      57             :  */
      58             : static bool rnh_hide_backups;
      59             : 
      60             : static void free_state(vrf_id_t vrf_id, struct route_entry *re,
      61             :                        struct route_node *rn);
      62             : static void copy_state(struct rnh *rnh, const struct route_entry *re,
      63             :                        struct route_node *rn);
      64             : static bool compare_state(struct route_entry *r1, struct route_entry *r2);
      65             : static void print_rnh(struct route_node *rn, struct vty *vty,
      66             :                       json_object *json);
      67             : static int zebra_client_cleanup_rnh(struct zserv *client);
      68             : 
      69           1 : void zebra_rnh_init(void)
      70             : {
      71           1 :         hook_register(zserv_client_close, zebra_client_cleanup_rnh);
      72           1 : }
      73             : 
      74          11 : static inline struct route_table *get_rnh_table(vrf_id_t vrfid, afi_t afi,
      75             :                                                 safi_t safi)
      76             : {
      77          11 :         struct zebra_vrf *zvrf;
      78          11 :         struct route_table *t = NULL;
      79             : 
      80          11 :         zvrf = zebra_vrf_lookup_by_id(vrfid);
      81          11 :         if (zvrf) {
      82          11 :                 if (safi == SAFI_UNICAST)
      83           7 :                         t = zvrf->rnh_table[afi];
      84           4 :                 else if (safi == SAFI_MULTICAST)
      85           4 :                         t = zvrf->rnh_table_multicast[afi];
      86             :         }
      87             : 
      88          11 :         return t;
      89             : }
      90             : 
      91           2 : static void zebra_rnh_remove_from_routing_table(struct rnh *rnh)
      92             : {
      93           2 :         struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id);
      94           2 :         struct route_table *table = zvrf->table[rnh->afi][rnh->safi];
      95           2 :         struct route_node *rn;
      96           2 :         rib_dest_t *dest;
      97             : 
      98           2 :         if (!table)
      99             :                 return;
     100             : 
     101           2 :         rn = route_node_match(table, &rnh->resolved_route);
     102           2 :         if (!rn)
     103             :                 return;
     104             : 
     105           2 :         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     106           2 :                 zlog_debug("%s: %s(%u):%pRN removed from tracking on %pRN",
     107             :                            __func__, VRF_LOGNAME(zvrf->vrf), rnh->vrf_id,
     108             :                            rnh->node, rn);
     109             : 
     110           2 :         dest = rib_dest_from_rnode(rn);
     111           2 :         rnh_list_del(&dest->nht, rnh);
     112           2 :         route_unlock_node(rn);
     113             : }
     114             : 
     115           2 : static void zebra_rnh_store_in_routing_table(struct rnh *rnh)
     116             : {
     117           2 :         struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id);
     118           2 :         struct route_table *table = zvrf->table[rnh->afi][rnh->safi];
     119           2 :         struct route_node *rn;
     120           2 :         rib_dest_t *dest;
     121             : 
     122           2 :         rn = route_node_match(table, &rnh->resolved_route);
     123           2 :         if (!rn)
     124             :                 return;
     125             : 
     126           2 :         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     127           2 :                 zlog_debug("%s: %s(%u):%pRN added for tracking on %pRN",
     128             :                            __func__, VRF_LOGNAME(zvrf->vrf), rnh->vrf_id,
     129             :                            rnh->node, rn);
     130             : 
     131           2 :         dest = rib_dest_from_rnode(rn);
     132           2 :         rnh_list_add_tail(&dest->nht, rnh);
     133           2 :         route_unlock_node(rn);
     134             : }
     135             : 
     136           1 : struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi,
     137             :                           bool *exists)
     138             : {
     139           1 :         struct route_table *table;
     140           1 :         struct route_node *rn;
     141           1 :         struct rnh *rnh = NULL;
     142           1 :         afi_t afi = family2afi(p->family);
     143             : 
     144           1 :         if (IS_ZEBRA_DEBUG_NHT) {
     145           1 :                 struct vrf *vrf = vrf_lookup_by_id(vrfid);
     146             : 
     147           1 :                 zlog_debug("%s(%u): Add RNH %pFX for safi: %u",
     148             :                            VRF_LOGNAME(vrf), vrfid, p, safi);
     149             :         }
     150             : 
     151           1 :         table = get_rnh_table(vrfid, afi, safi);
     152           1 :         if (!table) {
     153           0 :                 struct vrf *vrf = vrf_lookup_by_id(vrfid);
     154             : 
     155           0 :                 flog_warn(EC_ZEBRA_RNH_NO_TABLE,
     156             :                           "%s(%u): Add RNH %pFX - table not found",
     157             :                           VRF_LOGNAME(vrf), vrfid, p);
     158           0 :                 *exists = false;
     159           0 :                 return NULL;
     160             :         }
     161             : 
     162             :         /* Make it sure prefixlen is applied to the prefix. */
     163           1 :         apply_mask(p);
     164             : 
     165             :         /* Lookup (or add) route node.*/
     166           1 :         rn = route_node_get(table, p);
     167             : 
     168           1 :         if (!rn->info) {
     169           1 :                 rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh));
     170             : 
     171             :                 /*
     172             :                  * The resolved route is already 0.0.0.0/0 or
     173             :                  * 0::0/0 due to the calloc right above, but
     174             :                  * we should set the family so that future
     175             :                  * comparisons can just be done
     176             :                  */
     177           1 :                 rnh->resolved_route.family = p->family;
     178           1 :                 rnh->client_list = list_new();
     179           1 :                 rnh->vrf_id = vrfid;
     180           1 :                 rnh->seqno = 0;
     181           1 :                 rnh->afi = afi;
     182           1 :                 rnh->safi = safi;
     183           1 :                 rnh->zebra_pseudowire_list = list_new();
     184           1 :                 route_lock_node(rn);
     185           1 :                 rn->info = rnh;
     186           1 :                 rnh->node = rn;
     187           1 :                 *exists = false;
     188             : 
     189           1 :                 zebra_rnh_store_in_routing_table(rnh);
     190             :         } else
     191           0 :                 *exists = true;
     192             : 
     193           1 :         route_unlock_node(rn);
     194           1 :         return (rn->info);
     195             : }
     196             : 
     197           1 : struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi)
     198             : {
     199           1 :         struct route_table *table;
     200           1 :         struct route_node *rn;
     201             : 
     202           1 :         table = get_rnh_table(vrfid, family2afi(PREFIX_FAMILY(p)), safi);
     203           1 :         if (!table)
     204             :                 return NULL;
     205             : 
     206             :         /* Make it sure prefixlen is applied to the prefix. */
     207           1 :         apply_mask(p);
     208             : 
     209             :         /* Lookup route node.*/
     210           1 :         rn = route_node_lookup(table, p);
     211           1 :         if (!rn)
     212             :                 return NULL;
     213             : 
     214           1 :         route_unlock_node(rn);
     215           1 :         return (rn->info);
     216             : }
     217             : 
     218           1 : void zebra_free_rnh(struct rnh *rnh)
     219             : {
     220           1 :         struct zebra_vrf *zvrf;
     221           1 :         struct route_table *table;
     222             : 
     223           1 :         zebra_rnh_remove_from_routing_table(rnh);
     224           1 :         rnh->flags |= ZEBRA_NHT_DELETED;
     225           1 :         list_delete(&rnh->client_list);
     226           1 :         list_delete(&rnh->zebra_pseudowire_list);
     227             : 
     228           1 :         zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id);
     229           1 :         table = zvrf->table[family2afi(rnh->resolved_route.family)][rnh->safi];
     230             : 
     231           1 :         if (table) {
     232           1 :                 struct route_node *rern;
     233             : 
     234           1 :                 rern = route_node_match(table, &rnh->resolved_route);
     235           1 :                 if (rern) {
     236           1 :                         rib_dest_t *dest;
     237             : 
     238           1 :                         route_unlock_node(rern);
     239             : 
     240           1 :                         dest = rib_dest_from_rnode(rern);
     241           1 :                         rnh_list_del(&dest->nht, rnh);
     242             :                 }
     243             :         }
     244           1 :         free_state(rnh->vrf_id, rnh->state, rnh->node);
     245           1 :         XFREE(MTYPE_RNH, rnh);
     246           1 : }
     247             : 
     248           1 : static void zebra_delete_rnh(struct rnh *rnh)
     249             : {
     250           1 :         struct route_node *rn;
     251             : 
     252           1 :         if (!list_isempty(rnh->client_list)
     253           1 :             || !list_isempty(rnh->zebra_pseudowire_list))
     254             :                 return;
     255             : 
     256           1 :         if ((rnh->flags & ZEBRA_NHT_DELETED) || !(rn = rnh->node))
     257             :                 return;
     258             : 
     259           1 :         if (IS_ZEBRA_DEBUG_NHT) {
     260           1 :                 struct vrf *vrf = vrf_lookup_by_id(rnh->vrf_id);
     261             : 
     262           1 :                 zlog_debug("%s(%u): Del RNH %pRN", VRF_LOGNAME(vrf),
     263             :                            rnh->vrf_id, rnh->node);
     264             :         }
     265             : 
     266           1 :         zebra_free_rnh(rnh);
     267           1 :         rn->info = NULL;
     268           1 :         route_unlock_node(rn);
     269             : }
     270             : 
     271             : /*
     272             :  * This code will send to the registering client
     273             :  * the looked up rnh.
     274             :  * For a rnh that was created, there is no data
     275             :  * so it will send an empty nexthop group
     276             :  * If rnh exists then we know it has been evaluated
     277             :  * and as such it will have a resolved rnh.
     278             :  */
     279           1 : void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
     280             :                           vrf_id_t vrf_id)
     281             : {
     282           1 :         if (IS_ZEBRA_DEBUG_NHT) {
     283           1 :                 struct vrf *vrf = vrf_lookup_by_id(vrf_id);
     284             : 
     285           1 :                 zlog_debug("%s(%u): Client %s registers for RNH %pRN",
     286             :                            VRF_LOGNAME(vrf), vrf_id,
     287             :                            zebra_route_string(client->proto), rnh->node);
     288             :         }
     289           1 :         if (!listnode_lookup(rnh->client_list, client))
     290           1 :                 listnode_add(rnh->client_list, client);
     291             : 
     292             :         /*
     293             :          * We always need to respond with known information,
     294             :          * currently multiple daemons expect this behavior
     295             :          */
     296           1 :         zebra_send_rnh_update(rnh, client, vrf_id, 0);
     297           1 : }
     298             : 
     299           1 : void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client)
     300             : {
     301           1 :         if (IS_ZEBRA_DEBUG_NHT) {
     302           1 :                 struct vrf *vrf = vrf_lookup_by_id(rnh->vrf_id);
     303             : 
     304           1 :                 zlog_debug("Client %s unregisters for RNH %s(%u)%pRN",
     305             :                            zebra_route_string(client->proto), VRF_LOGNAME(vrf),
     306             :                            vrf->vrf_id, rnh->node);
     307             :         }
     308           1 :         listnode_delete(rnh->client_list, client);
     309           1 :         zebra_delete_rnh(rnh);
     310           1 : }
     311             : 
     312             : /* XXX move this utility function elsewhere? */
     313           0 : static void addr2hostprefix(int af, const union g_addr *addr,
     314             :                             struct prefix *prefix)
     315             : {
     316           0 :         switch (af) {
     317           0 :         case AF_INET:
     318           0 :                 prefix->family = AF_INET;
     319           0 :                 prefix->prefixlen = IPV4_MAX_BITLEN;
     320           0 :                 prefix->u.prefix4 = addr->ipv4;
     321           0 :                 break;
     322           0 :         case AF_INET6:
     323           0 :                 prefix->family = AF_INET6;
     324           0 :                 prefix->prefixlen = IPV6_MAX_BITLEN;
     325           0 :                 prefix->u.prefix6 = addr->ipv6;
     326           0 :                 break;
     327           0 :         default:
     328           0 :                 memset(prefix, 0, sizeof(*prefix));
     329           0 :                 zlog_warn("%s: unknown address family %d", __func__, af);
     330           0 :                 break;
     331             :         }
     332           0 : }
     333             : 
     334           0 : void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw,
     335             :                                    bool *nht_exists)
     336             : {
     337           0 :         struct prefix nh;
     338           0 :         struct rnh *rnh;
     339           0 :         bool exists;
     340           0 :         struct zebra_vrf *zvrf;
     341             : 
     342           0 :         *nht_exists = false;
     343             : 
     344           0 :         zvrf = vrf_info_lookup(vrf_id);
     345           0 :         if (!zvrf)
     346           0 :                 return;
     347             : 
     348           0 :         addr2hostprefix(pw->af, &pw->nexthop, &nh);
     349           0 :         rnh = zebra_add_rnh(&nh, vrf_id, SAFI_UNICAST, &exists);
     350           0 :         if (!rnh)
     351             :                 return;
     352             : 
     353           0 :         if (!listnode_lookup(rnh->zebra_pseudowire_list, pw)) {
     354           0 :                 listnode_add(rnh->zebra_pseudowire_list, pw);
     355           0 :                 pw->rnh = rnh;
     356           0 :                 zebra_evaluate_rnh(zvrf, family2afi(pw->af), 1, &nh,
     357             :                                    SAFI_UNICAST);
     358             :         } else
     359           0 :                 *nht_exists = true;
     360             : }
     361             : 
     362           0 : void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw)
     363             : {
     364           0 :         struct rnh *rnh;
     365             : 
     366           0 :         rnh = pw->rnh;
     367           0 :         if (!rnh)
     368             :                 return;
     369             : 
     370           0 :         listnode_delete(rnh->zebra_pseudowire_list, pw);
     371           0 :         pw->rnh = NULL;
     372             : 
     373           0 :         zebra_delete_rnh(rnh);
     374             : }
     375             : 
     376             : /* Clear the NEXTHOP_FLAG_RNH_FILTERED flags on all nexthops
     377             :  */
     378           1 : static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry *re)
     379             : {
     380           1 :         struct nexthop *nexthop;
     381             : 
     382           1 :         if (re) {
     383           2 :                 for (nexthop = re->nhe->nhg.nexthop; nexthop;
     384           1 :                      nexthop = nexthop->next) {
     385           1 :                         UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED);
     386             :                 }
     387             :         }
     388           1 : }
     389             : 
     390             : /* Apply the NHT route-map for a client to the route (and nexthops)
     391             :  * resolving a NH.
     392             :  */
     393           0 : static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf,
     394             :                                     struct route_node *prn,
     395             :                                     struct route_entry *re, int proto)
     396             : {
     397           0 :         int at_least_one = 0;
     398           0 :         struct nexthop *nexthop;
     399           0 :         route_map_result_t ret;
     400             : 
     401           0 :         if (prn && re) {
     402           0 :                 for (nexthop = re->nhe->nhg.nexthop; nexthop;
     403           0 :                      nexthop = nexthop->next) {
     404           0 :                         ret = zebra_nht_route_map_check(
     405           0 :                                 afi, proto, &prn->p, zvrf, re, nexthop);
     406           0 :                         if (ret != RMAP_DENYMATCH)
     407           0 :                                 at_least_one++; /* at least one valid NH */
     408             :                         else {
     409           0 :                                 SET_FLAG(nexthop->flags,
     410             :                                          NEXTHOP_FLAG_RNH_FILTERED);
     411             :                         }
     412             :                 }
     413             :         }
     414           0 :         return (at_least_one);
     415             : }
     416             : 
     417             : /*
     418             :  * Notify clients registered for this nexthop about a change.
     419             :  */
     420           1 : static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi,
     421             :                                               struct route_node *nrn,
     422             :                                               struct rnh *rnh,
     423             :                                               struct route_node *prn,
     424             :                                               struct route_entry *re)
     425             : {
     426           1 :         struct listnode *node;
     427           1 :         struct zserv *client;
     428           1 :         int num_resolving_nh;
     429             : 
     430           1 :         if (IS_ZEBRA_DEBUG_NHT) {
     431           1 :                 if (prn && re) {
     432           1 :                         zlog_debug("%s(%u):%pRN: NH resolved over route %pRN",
     433             :                                    VRF_LOGNAME(zvrf->vrf), zvrf->vrf->vrf_id,
     434             :                                    nrn, prn);
     435             :                 } else
     436           0 :                         zlog_debug("%s(%u):%pRN: NH has become unresolved",
     437             :                                    VRF_LOGNAME(zvrf->vrf), zvrf->vrf->vrf_id,
     438             :                                    nrn);
     439             :         }
     440             : 
     441           2 :         for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
     442           0 :                 if (prn && re) {
     443             :                         /* Apply route-map for this client to route resolving
     444             :                          * this
     445             :                          * nexthop to see if it is filtered or not.
     446             :                          */
     447           0 :                         zebra_rnh_clear_nexthop_rnh_filters(re);
     448           0 :                         num_resolving_nh = zebra_rnh_apply_nht_rmap(
     449           0 :                                 afi, zvrf, prn, re, client->proto);
     450           0 :                         if (num_resolving_nh)
     451           0 :                                 rnh->filtered[client->proto] = 0;
     452             :                         else
     453           0 :                                 rnh->filtered[client->proto] = 1;
     454             : 
     455           0 :                         if (IS_ZEBRA_DEBUG_NHT)
     456           0 :                                 zlog_debug(
     457             :                                         "%s(%u):%pRN: Notifying client %s about NH %s",
     458             :                                         VRF_LOGNAME(zvrf->vrf),
     459             :                                         zvrf->vrf->vrf_id, nrn,
     460             :                                         zebra_route_string(client->proto),
     461             :                                         num_resolving_nh
     462             :                                                 ? ""
     463             :                                                 : "(filtered by route-map)");
     464             :                 } else {
     465           0 :                         rnh->filtered[client->proto] = 0;
     466           0 :                         if (IS_ZEBRA_DEBUG_NHT)
     467           0 :                                 zlog_debug(
     468             :                                         "%s(%u):%pRN: Notifying client %s about NH (unreachable)",
     469             :                                         VRF_LOGNAME(zvrf->vrf),
     470             :                                         zvrf->vrf->vrf_id, nrn,
     471             :                                         zebra_route_string(client->proto));
     472             :                 }
     473             : 
     474           0 :                 zebra_send_rnh_update(rnh, client, zvrf->vrf->vrf_id, 0);
     475             :         }
     476             : 
     477           1 :         if (re)
     478           1 :                 zebra_rnh_clear_nexthop_rnh_filters(re);
     479           1 : }
     480             : 
     481             : /*
     482             :  * Utility to determine whether a candidate nexthop is useable. We make this
     483             :  * check in a couple of places, so this is a single home for the logic we
     484             :  * use.
     485             :  */
     486             : 
     487             : static const int RNH_INVALID_NH_FLAGS = (NEXTHOP_FLAG_RECURSIVE |
     488             :                                          NEXTHOP_FLAG_DUPLICATE |
     489             :                                          NEXTHOP_FLAG_RNH_FILTERED);
     490             : 
     491           4 : bool rnh_nexthop_valid(const struct route_entry *re, const struct nexthop *nh)
     492             : {
     493           4 :         return (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)
     494           4 :                 && CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)
     495           4 :                 && !CHECK_FLAG(nh->flags, RNH_INVALID_NH_FLAGS));
     496             : }
     497             : 
     498             : /*
     499             :  * Determine whether an re's nexthops are valid for tracking.
     500             :  */
     501           1 : static bool rnh_check_re_nexthops(const struct route_entry *re,
     502             :                                   const struct rnh *rnh)
     503             : {
     504           1 :         bool ret = false;
     505           1 :         const struct nexthop *nexthop = NULL;
     506             : 
     507             :         /* Check route's nexthops */
     508           1 :         for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
     509           1 :                 if (rnh_nexthop_valid(re, nexthop))
     510             :                         break;
     511             :         }
     512             : 
     513             :         /* Check backup nexthops, if any. */
     514           1 :         if (nexthop == NULL && re->nhe->backup_info &&
     515           0 :             re->nhe->backup_info->nhe) {
     516           0 :                 for (ALL_NEXTHOPS(re->nhe->backup_info->nhe->nhg, nexthop)) {
     517           0 :                         if (rnh_nexthop_valid(re, nexthop))
     518             :                                 break;
     519             :                 }
     520             :         }
     521             : 
     522           0 :         if (nexthop == NULL) {
     523           0 :                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     524           0 :                         zlog_debug(
     525             :                                 "        Route Entry %s no nexthops",
     526             :                                 zebra_route_string(re->type));
     527             : 
     528           0 :                 goto done;
     529             :         }
     530             : 
     531             :         /* Some special checks if registration asked for them. */
     532           1 :         if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) {
     533           0 :                 if ((re->type == ZEBRA_ROUTE_CONNECT)
     534           0 :                     || (re->type == ZEBRA_ROUTE_STATIC))
     535           0 :                         ret = true;
     536           0 :                 if (re->type == ZEBRA_ROUTE_NHRP) {
     537             : 
     538           0 :                         for (nexthop = re->nhe->nhg.nexthop;
     539           0 :                              nexthop;
     540           0 :                              nexthop = nexthop->next)
     541           0 :                                 if (nexthop->type == NEXTHOP_TYPE_IFINDEX)
     542             :                                         break;
     543           0 :                         if (nexthop)
     544           1 :                                 ret = true;
     545             :                 }
     546             :         } else {
     547             :                 ret = true;
     548             :         }
     549             : 
     550           1 : done:
     551           1 :         return ret;
     552             : }
     553             : 
     554             : /*
     555             :  * Determine appropriate route (route entry) resolving a tracked
     556             :  * nexthop.
     557             :  */
     558             : static struct route_entry *
     559           1 : zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
     560             :                                 struct route_node *nrn, const struct rnh *rnh,
     561             :                                 struct route_node **prn)
     562             : {
     563           1 :         struct route_table *route_table;
     564           1 :         struct route_node *rn;
     565           1 :         struct route_entry *re;
     566             : 
     567           1 :         *prn = NULL;
     568             : 
     569           1 :         route_table = zvrf->table[afi][rnh->safi];
     570           1 :         if (!route_table)
     571             :                 return NULL;
     572             : 
     573           1 :         rn = route_node_match(route_table, &nrn->p);
     574           1 :         if (!rn)
     575             :                 return NULL;
     576             : 
     577             :         /* Unlock route node - we don't need to lock when walking the tree. */
     578           1 :         route_unlock_node(rn);
     579             : 
     580             :         /* While resolving nexthops, we may need to walk up the tree from the
     581             :          * most-specific match. Do similar logic as in zebra_rib.c
     582             :          */
     583           1 :         while (rn) {
     584           1 :                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     585           1 :                         zlog_debug("%s: %s(%u):%pRN Possible Match to %pRN",
     586             :                                    __func__, VRF_LOGNAME(zvrf->vrf),
     587             :                                    rnh->vrf_id, rnh->node, rn);
     588             : 
     589             :                 /* Do not resolve over default route unless allowed &&
     590             :                  * match route to be exact if so specified
     591             :                  */
     592           1 :                 if (is_default_prefix(&rn->p)
     593           0 :                     && (!CHECK_FLAG(rnh->flags, ZEBRA_NHT_RESOLVE_VIA_DEFAULT)
     594           1 :                         && !rnh_resolve_via_default(zvrf, rn->p.family))) {
     595           0 :                         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     596           0 :                                 zlog_debug(
     597             :                                         "        Not allowed to resolve through default prefix: rnh->resolve_via_default: %u",
     598             :                                         CHECK_FLAG(
     599             :                                                 rnh->flags,
     600             :                                                 ZEBRA_NHT_RESOLVE_VIA_DEFAULT));
     601           0 :                         return NULL;
     602             :                 }
     603             : 
     604             :                 /* Identify appropriate route entry. */
     605           2 :                 RNODE_FOREACH_RE (rn, re) {
     606           1 :                         if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) {
     607           0 :                                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     608           0 :                                         zlog_debug(
     609             :                                                 "        Route Entry %s removed",
     610             :                                                 zebra_route_string(re->type));
     611           0 :                                 continue;
     612             :                         }
     613           1 :                         if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) &&
     614             :                             !CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) {
     615           0 :                                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     616           0 :                                         zlog_debug(
     617             :                                                 "        Route Entry %s !selected",
     618             :                                                 zebra_route_string(re->type));
     619           0 :                                 continue;
     620             :                         }
     621             : 
     622           1 :                         if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED)) {
     623           0 :                                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     624           0 :                                         zlog_debug(
     625             :                                                 "        Route Entry %s queued",
     626             :                                                 zebra_route_string(re->type));
     627           0 :                                 continue;
     628             :                         }
     629             : 
     630             :                         /* Just being SELECTED isn't quite enough - must
     631             :                          * have an installed nexthop to be useful.
     632             :                          */
     633           1 :                         if (rnh_check_re_nexthops(re, rnh))
     634             :                                 break;
     635             :                 }
     636             : 
     637             :                 /* Route entry found, we're done; else, walk up the tree. */
     638           1 :                 if (re) {
     639           1 :                         *prn = rn;
     640           1 :                         return re;
     641             :                 } else {
     642             :                         /* Resolve the nexthop recursively by finding matching
     643             :                          * route with lower prefix length
     644             :                          */
     645           0 :                         rn = rn->parent;
     646             :                 }
     647             :         }
     648             : 
     649             :         return NULL;
     650             : }
     651             : 
     652           1 : static void zebra_rnh_process_pseudowires(vrf_id_t vrfid, struct rnh *rnh)
     653             : {
     654           1 :         struct zebra_pw *pw;
     655           1 :         struct listnode *node;
     656             : 
     657           2 :         for (ALL_LIST_ELEMENTS_RO(rnh->zebra_pseudowire_list, node, pw))
     658           0 :                 zebra_pw_update(pw);
     659           1 : }
     660             : 
     661             : /*
     662             :  * See if a tracked nexthop entry has undergone any change, and if so,
     663             :  * take appropriate action; this involves notifying any clients and/or
     664             :  * scheduling dependent static routes for processing.
     665             :  */
     666           1 : static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
     667             :                                          int force, struct route_node *nrn,
     668             :                                          struct rnh *rnh,
     669             :                                          struct route_node *prn,
     670             :                                          struct route_entry *re)
     671             : {
     672           1 :         int state_changed = 0;
     673             : 
     674             :         /* If we're resolving over a different route, resolution has changed or
     675             :          * the resolving route has some change (e.g., metric), there is a state
     676             :          * change.
     677             :          */
     678           1 :         zebra_rnh_remove_from_routing_table(rnh);
     679           1 :         if (!prefix_same(&rnh->resolved_route, prn ? &prn->p : NULL)) {
     680           1 :                 if (prn)
     681           1 :                         prefix_copy(&rnh->resolved_route, &prn->p);
     682             :                 else {
     683             :                         /*
     684             :                          * Just quickly store the family of the resolved
     685             :                          * route so that we can reset it in a second here
     686             :                          */
     687           0 :                         int family = rnh->resolved_route.family;
     688             : 
     689           0 :                         memset(&rnh->resolved_route, 0, sizeof(struct prefix));
     690           0 :                         rnh->resolved_route.family = family;
     691             :                 }
     692             : 
     693           1 :                 copy_state(rnh, re, nrn);
     694           1 :                 state_changed = 1;
     695           0 :         } else if (compare_state(re, rnh->state)) {
     696           0 :                 copy_state(rnh, re, nrn);
     697           0 :                 state_changed = 1;
     698             :         }
     699           1 :         zebra_rnh_store_in_routing_table(rnh);
     700             : 
     701           1 :         if (state_changed || force) {
     702             :                 /* NOTE: Use the "copy" of resolving route stored in 'rnh' i.e.,
     703             :                  * rnh->state.
     704             :                  */
     705             :                 /* Notify registered protocol clients. */
     706           1 :                 zebra_rnh_notify_protocol_clients(zvrf, afi, nrn, rnh, prn,
     707             :                                                   rnh->state);
     708             : 
     709             :                 /* Process pseudowires attached to this nexthop */
     710           1 :                 zebra_rnh_process_pseudowires(zvrf->vrf->vrf_id, rnh);
     711             :         }
     712           1 : }
     713             : 
     714             : /* Evaluate one tracked entry */
     715           1 : static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi,
     716             :                                      int force, struct route_node *nrn)
     717             : {
     718           1 :         struct rnh *rnh;
     719           1 :         struct route_entry *re;
     720           1 :         struct route_node *prn;
     721             : 
     722           1 :         if (IS_ZEBRA_DEBUG_NHT) {
     723           1 :                 zlog_debug("%s(%u):%pRN: Evaluate RNH, %s",
     724             :                            VRF_LOGNAME(zvrf->vrf), zvrf->vrf->vrf_id, nrn,
     725             :                            force ? "(force)" : "");
     726             :         }
     727             : 
     728           1 :         rnh = nrn->info;
     729             : 
     730             :         /* Identify route entry (RE) resolving this tracked entry. */
     731           1 :         re = zebra_rnh_resolve_nexthop_entry(zvrf, afi, nrn, rnh, &prn);
     732             : 
     733             :         /* If the entry cannot be resolved and that is also the existing state,
     734             :          * there is nothing further to do.
     735             :          */
     736           1 :         if (!re && rnh->state == NULL && !force)
     737           0 :                 return;
     738             : 
     739             :         /* Process based on type of entry. */
     740           1 :         zebra_rnh_eval_nexthop_entry(zvrf, afi, force, nrn, rnh, prn, re);
     741             : }
     742             : 
     743             : /*
     744             :  * Clear the ROUTE_ENTRY_NEXTHOPS_CHANGED flag
     745             :  * from the re entries.
     746             :  *
     747             :  * Please note we are doing this *after* we have
     748             :  * notified the world about each nexthop as that
     749             :  * we can have a situation where one re entry
     750             :  * covers multiple nexthops we are interested in.
     751             :  */
     752           0 : static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi,
     753             :                                      struct route_node *nrn)
     754             : {
     755           0 :         struct rnh *rnh;
     756           0 :         struct route_entry *re;
     757           0 :         struct route_node *prn;
     758             : 
     759           0 :         rnh = nrn->info;
     760             : 
     761             :         /* Identify route entry (RIB) resolving this tracked entry. */
     762           0 :         re = zebra_rnh_resolve_nexthop_entry(zvrf, afi, nrn, rnh, &prn);
     763             : 
     764           0 :         if (re)
     765           0 :                 UNSET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED);
     766           0 : }
     767             : 
     768             : /* Evaluate all tracked entries (nexthops or routes for import into BGP)
     769             :  * of a particular VRF and address-family or a specific prefix.
     770             :  */
     771           1 : void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force,
     772             :                         const struct prefix *p, safi_t safi)
     773             : {
     774           1 :         struct route_table *rnh_table;
     775           1 :         struct route_node *nrn;
     776             : 
     777           1 :         rnh_table = get_rnh_table(zvrf->vrf->vrf_id, afi, safi);
     778           1 :         if (!rnh_table) // unexpected
     779             :                 return;
     780             : 
     781           1 :         if (p) {
     782             :                 /* Evaluating a specific entry, make sure it exists. */
     783           1 :                 nrn = route_node_lookup(rnh_table, p);
     784           1 :                 if (nrn && nrn->info)
     785           1 :                         zebra_rnh_evaluate_entry(zvrf, afi, force, nrn);
     786             : 
     787           1 :                 if (nrn)
     788           1 :                         route_unlock_node(nrn);
     789             :         } else {
     790             :                 /* Evaluate entire table. */
     791           0 :                 nrn = route_top(rnh_table);
     792           0 :                 while (nrn) {
     793           0 :                         if (nrn->info)
     794           0 :                                 zebra_rnh_evaluate_entry(zvrf, afi, force, nrn);
     795           0 :                         nrn = route_next(nrn); /* this will also unlock nrn */
     796             :                 }
     797           0 :                 nrn = route_top(rnh_table);
     798           0 :                 while (nrn) {
     799           0 :                         if (nrn->info)
     800           0 :                                 zebra_rnh_clear_nhc_flag(zvrf, afi, nrn);
     801           0 :                         nrn = route_next(nrn); /* this will also unlock nrn */
     802             :                 }
     803             :         }
     804             : }
     805             : 
     806           0 : void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, safi_t safi,
     807             :                            struct vty *vty, const struct prefix *p,
     808             :                            json_object *json)
     809             : {
     810           0 :         struct route_table *table;
     811           0 :         struct route_node *rn;
     812             : 
     813           0 :         table = get_rnh_table(vrfid, afi, safi);
     814           0 :         if (!table) {
     815           0 :                 if (IS_ZEBRA_DEBUG_NHT)
     816           0 :                         zlog_debug("print_rnhs: rnh table not found");
     817           0 :                 return;
     818             :         }
     819             : 
     820           0 :         for (rn = route_top(table); rn; rn = route_next(rn)) {
     821           0 :                 if (p && !prefix_match(&rn->p, p))
     822           0 :                         continue;
     823             : 
     824           0 :                 if (rn->info)
     825           0 :                         print_rnh(rn, vty, json);
     826             :         }
     827             : }
     828             : 
     829             : /**
     830             :  * free_state - free up the re structure associated with the rnh.
     831             :  */
     832           1 : static void free_state(vrf_id_t vrf_id, struct route_entry *re,
     833             :                        struct route_node *rn)
     834             : {
     835           1 :         if (!re)
     836             :                 return;
     837             : 
     838             :         /* free RE and nexthops */
     839           1 :         zebra_nhg_free(re->nhe);
     840           1 :         XFREE(MTYPE_RE, re);
     841             : }
     842             : 
     843           1 : static void copy_state(struct rnh *rnh, const struct route_entry *re,
     844             :                        struct route_node *rn)
     845             : {
     846           1 :         struct route_entry *state;
     847             : 
     848           1 :         if (rnh->state) {
     849           0 :                 free_state(rnh->vrf_id, rnh->state, rn);
     850           0 :                 rnh->state = NULL;
     851             :         }
     852             : 
     853           1 :         if (!re)
     854             :                 return;
     855             : 
     856           1 :         state = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
     857           1 :         state->type = re->type;
     858           1 :         state->distance = re->distance;
     859           1 :         state->metric = re->metric;
     860           1 :         state->vrf_id = re->vrf_id;
     861           1 :         state->status = re->status;
     862             : 
     863           1 :         state->nhe = zebra_nhe_copy(re->nhe, 0);
     864             : 
     865             :         /* Copy the 'fib' nexthops also, if present - we want to capture
     866             :          * the true installed nexthops.
     867             :          */
     868           1 :         if (re->fib_ng.nexthop)
     869           0 :                 nexthop_group_copy(&state->fib_ng, &re->fib_ng);
     870           1 :         if (re->fib_backup_ng.nexthop)
     871           0 :                 nexthop_group_copy(&state->fib_backup_ng, &re->fib_backup_ng);
     872             : 
     873           1 :         rnh->state = state;
     874             : }
     875             : 
     876             : /*
     877             :  * Locate the next primary nexthop, used when comparing current rnh info with
     878             :  * an updated route.
     879             :  */
     880           0 : static struct nexthop *next_valid_primary_nh(struct route_entry *re,
     881             :                                              struct nexthop *nh)
     882             : {
     883           0 :         struct nexthop_group *nhg;
     884           0 :         struct nexthop *bnh;
     885           0 :         int i, idx;
     886           0 :         bool default_path = true;
     887             : 
     888             :         /* Fib backup ng present: some backups are installed,
     889             :          * and we're configured for special handling if there are backups.
     890             :          */
     891           0 :         if (rnh_hide_backups && (re->fib_backup_ng.nexthop != NULL))
     892           0 :                 default_path = false;
     893             : 
     894             :         /* Default path: no special handling, just using the 'installed'
     895             :          * primary nexthops and the common validity test.
     896             :          */
     897           0 :         if (default_path) {
     898           0 :                 if (nh == NULL) {
     899           0 :                         nhg = rib_get_fib_nhg(re);
     900           0 :                         nh = nhg->nexthop;
     901             :                 } else
     902           0 :                         nh = nexthop_next(nh);
     903             : 
     904           0 :                 while (nh) {
     905           0 :                         if (rnh_nexthop_valid(re, nh))
     906             :                                 break;
     907             :                         else
     908           0 :                                 nh = nexthop_next(nh);
     909             :                 }
     910             : 
     911           0 :                 return nh;
     912             :         }
     913             : 
     914             :         /* Hide backup activation/switchover events.
     915             :          *
     916             :          * If we've had a switchover, an inactive primary won't be in
     917             :          * the fib list at all - the 'fib' list could even be empty
     918             :          * in the case where no primary is installed. But we want to consider
     919             :          * those primaries "valid" if they have an activated backup nh.
     920             :          *
     921             :          * The logic is something like:
     922             :          * if (!fib_nhg)
     923             :          *     // then all primaries are installed
     924             :          * else
     925             :          *     for each primary in re nhg
     926             :          *         if in fib_nhg
     927             :          *             primary is installed
     928             :          *         else if a backup is installed
     929             :          *             primary counts as installed
     930             :          *         else
     931             :          *             primary !installed
     932             :          */
     933             : 
     934             :         /* Start with the first primary */
     935           0 :         if (nh == NULL)
     936           0 :                 nh = re->nhe->nhg.nexthop;
     937             :         else
     938           0 :                 nh = nexthop_next(nh);
     939             : 
     940           0 :         while (nh) {
     941             : 
     942           0 :                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     943           0 :                         zlog_debug("%s: checking primary NH %pNHv",
     944             :                                    __func__, nh);
     945             : 
     946             :                 /* If this nexthop is in the fib list, it's installed */
     947           0 :                 nhg = rib_get_fib_nhg(re);
     948             : 
     949           0 :                 for (bnh = nhg->nexthop; bnh; bnh = nexthop_next(bnh)) {
     950           0 :                         if (nexthop_cmp(nh, bnh) == 0)
     951             :                                 break;
     952             :                 }
     953             : 
     954           0 :                 if (bnh != NULL) {
     955             :                         /* Found the match */
     956           0 :                         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     957           0 :                                 zlog_debug("%s:     NH in fib list", __func__);
     958             :                         break;
     959             :                 }
     960             : 
     961             :                 /* Else if this nexthop's backup is installed, it counts */
     962           0 :                 nhg = rib_get_fib_backup_nhg(re);
     963           0 :                 bnh = nhg->nexthop;
     964             : 
     965           0 :                 for (idx = 0; bnh != NULL; idx++) {
     966             :                         /* If we find an active backup nh for this
     967             :                          * primary, we're done;
     968             :                          */
     969           0 :                         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     970           0 :                                 zlog_debug("%s: checking backup %pNHv [%d]",
     971             :                                            __func__, bnh, idx);
     972             : 
     973           0 :                         if (!CHECK_FLAG(bnh->flags, NEXTHOP_FLAG_ACTIVE))
     974           0 :                                 continue;
     975             : 
     976           0 :                         for (i = 0; i < nh->backup_num; i++) {
     977             :                                 /* Found a matching activated backup nh */
     978           0 :                                 if (nh->backup_idx[i] == idx) {
     979           0 :                                         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
     980           0 :                                                 zlog_debug("%s: backup %d activated",
     981             :                                                            __func__, i);
     982             : 
     983           0 :                                         goto done;
     984             :                                 }
     985             :                         }
     986             : 
     987             :                         /* Note that we're not recursing here if the
     988             :                          * backups are recursive: the primary's index is
     989             :                          * only valid in the top-level backup list.
     990             :                          */
     991           0 :                         bnh = bnh->next;
     992             :                 }
     993             : 
     994             :                 /* Try the next primary nexthop */
     995           0 :                 nh = nexthop_next(nh);
     996             :         }
     997             : 
     998           0 : done:
     999             : 
    1000             :         return nh;
    1001             : }
    1002             : 
    1003             : /*
    1004             :  * Compare two route_entries' nexthops. Account for backup nexthops
    1005             :  * and for the 'fib' nexthop lists, if present.
    1006             :  */
    1007           0 : static bool compare_valid_nexthops(struct route_entry *r1,
    1008             :                                    struct route_entry *r2)
    1009             : {
    1010           0 :         bool matched_p = false;
    1011           0 :         struct nexthop_group *nhg1, *nhg2;
    1012           0 :         struct nexthop *nh1, *nh2;
    1013             : 
    1014             :         /* Start with the primary nexthops */
    1015             : 
    1016           0 :         nh1 = next_valid_primary_nh(r1, NULL);
    1017           0 :         nh2 = next_valid_primary_nh(r2, NULL);
    1018             : 
    1019           0 :         while (1) {
    1020             :                 /* Find any differences in the nexthop lists */
    1021             : 
    1022           0 :                 if (nh1 && nh2) {
    1023             :                         /* Any difference is a no-match */
    1024           0 :                         if (nexthop_cmp(nh1, nh2) != 0) {
    1025           0 :                                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
    1026           0 :                                         zlog_debug("%s: nh1: %pNHv, nh2: %pNHv differ",
    1027             :                                                    __func__, nh1, nh2);
    1028           0 :                                 goto done;
    1029             :                         }
    1030             : 
    1031           0 :                 } else if (nh1 || nh2) {
    1032             :                         /* One list has more valid nexthops than the other */
    1033           0 :                         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
    1034           0 :                                 zlog_debug("%s: nh1 %s, nh2 %s", __func__,
    1035             :                                            nh1 ? "non-NULL" : "NULL",
    1036             :                                            nh2 ? "non-NULL" : "NULL");
    1037           0 :                         goto done;
    1038             :                 } else
    1039             :                         break; /* Done with both lists */
    1040             : 
    1041           0 :                 nh1 = next_valid_primary_nh(r1, nh1);
    1042           0 :                 nh2 = next_valid_primary_nh(r2, nh2);
    1043             :         }
    1044             : 
    1045             :         /* If configured, don't compare installed backup state - we've
    1046             :          * accounted for that with the primaries above.
    1047             :          *
    1048             :          * But we do want to compare the routes' backup info,
    1049             :          * in case the owning route has changed the backups -
    1050             :          * that change we do want to report.
    1051             :          */
    1052           0 :         if (rnh_hide_backups) {
    1053           0 :                 uint32_t hash1 = 0, hash2 = 0;
    1054             : 
    1055           0 :                 if (r1->nhe->backup_info)
    1056           0 :                         hash1 = nexthop_group_hash(
    1057           0 :                                 &r1->nhe->backup_info->nhe->nhg);
    1058             : 
    1059           0 :                 if (r2->nhe->backup_info)
    1060           0 :                         hash2 = nexthop_group_hash(
    1061           0 :                                 &r2->nhe->backup_info->nhe->nhg);
    1062             : 
    1063           0 :                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
    1064           0 :                         zlog_debug("%s: backup hash1 %#x, hash2 %#x",
    1065             :                                    __func__, hash1, hash2);
    1066             : 
    1067           0 :                 if (hash1 != hash2)
    1068           0 :                         goto done;
    1069             :                 else
    1070           0 :                         goto finished;
    1071             :         }
    1072             : 
    1073             :         /* The test for the backups is slightly different: the only installed
    1074             :          * backups will be in the 'fib' list.
    1075             :          */
    1076           0 :         nhg1 = rib_get_fib_backup_nhg(r1);
    1077           0 :         nhg2 = rib_get_fib_backup_nhg(r2);
    1078             : 
    1079           0 :         nh1 = nhg1->nexthop;
    1080           0 :         nh2 = nhg2->nexthop;
    1081             : 
    1082           0 :         while (1) {
    1083             :                 /* Find each backup list's next valid nexthop */
    1084           0 :                 while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1))
    1085           0 :                         nh1 = nexthop_next(nh1);
    1086             : 
    1087           0 :                 while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2))
    1088           0 :                         nh2 = nexthop_next(nh2);
    1089             : 
    1090           0 :                 if (nh1 && nh2) {
    1091             :                         /* Any difference is a no-match */
    1092           0 :                         if (nexthop_cmp(nh1, nh2) != 0) {
    1093           0 :                                 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
    1094           0 :                                         zlog_debug("%s: backup nh1: %pNHv, nh2: %pNHv differ",
    1095             :                                                    __func__, nh1, nh2);
    1096           0 :                                 goto done;
    1097             :                         }
    1098             : 
    1099           0 :                         nh1 = nexthop_next(nh1);
    1100           0 :                         nh2 = nexthop_next(nh2);
    1101           0 :                 } else if (nh1 || nh2) {
    1102             :                         /* One list has more valid nexthops than the other */
    1103           0 :                         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
    1104           0 :                                 zlog_debug("%s: backup nh1 %s, nh2 %s",
    1105             :                                            __func__,
    1106             :                                            nh1 ? "non-NULL" : "NULL",
    1107             :                                            nh2 ? "non-NULL" : "NULL");
    1108           0 :                         goto done;
    1109             :                 } else
    1110             :                         break; /* Done with both lists */
    1111             :         }
    1112             : 
    1113           0 : finished:
    1114             : 
    1115             :         /* Well, it's a match */
    1116             :         matched_p = true;
    1117             : 
    1118           0 : done:
    1119             : 
    1120           0 :         if (IS_ZEBRA_DEBUG_NHT_DETAILED)
    1121           0 :                 zlog_debug("%s: %smatched",
    1122             :                            __func__, (matched_p ? "" : "NOT "));
    1123             : 
    1124           0 :         return matched_p;
    1125             : }
    1126             : 
    1127             : /* Returns 'false' if no difference. */
    1128           0 : static bool compare_state(struct route_entry *r1,
    1129             :                           struct route_entry *r2)
    1130             : {
    1131           0 :         if (!r1 && !r2)
    1132             :                 return false;
    1133             : 
    1134           0 :         if ((!r1 && r2) || (r1 && !r2))
    1135             :                 return true;
    1136             : 
    1137           0 :         if (r1->distance != r2->distance)
    1138             :                 return true;
    1139             : 
    1140           0 :         if (r1->metric != r2->metric)
    1141             :                 return true;
    1142             : 
    1143           0 :         if (!compare_valid_nexthops(r1, r2))
    1144             :                 return true;
    1145             : 
    1146             :         return false;
    1147             : }
    1148             : 
    1149           1 : int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client,
    1150             :                           vrf_id_t vrf_id, uint32_t srte_color)
    1151             : {
    1152           1 :         struct stream *s = NULL;
    1153           1 :         struct route_entry *re;
    1154           1 :         unsigned long nump;
    1155           1 :         uint8_t num;
    1156           1 :         struct nexthop *nh;
    1157           1 :         struct route_node *rn;
    1158           1 :         int ret;
    1159           1 :         uint32_t message = 0;
    1160             : 
    1161           1 :         rn = rnh->node;
    1162           1 :         re = rnh->state;
    1163             : 
    1164             :         /* Get output stream. */
    1165           1 :         s = stream_new(ZEBRA_MAX_PACKET_SIZ);
    1166             : 
    1167           1 :         zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, vrf_id);
    1168             : 
    1169             :         /* Message flags. */
    1170           1 :         if (srte_color)
    1171           0 :                 SET_FLAG(message, ZAPI_MESSAGE_SRTE);
    1172           1 :         stream_putl(s, message);
    1173             : 
    1174             :         /*
    1175             :          * Put what we were told to match against
    1176             :          */
    1177           1 :         stream_putw(s, rnh->safi);
    1178           1 :         stream_putw(s, rn->p.family);
    1179           1 :         stream_putc(s, rn->p.prefixlen);
    1180           1 :         switch (rn->p.family) {
    1181           0 :         case AF_INET:
    1182           0 :                 stream_put_in_addr(s, &rn->p.u.prefix4);
    1183           0 :                 break;
    1184           1 :         case AF_INET6:
    1185           1 :                 stream_put(s, &rn->p.u.prefix6, IPV6_MAX_BYTELEN);
    1186           1 :                 break;
    1187           0 :         default:
    1188           0 :                 flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY,
    1189             :                          "%s: Unknown family (%d) notification attempted",
    1190             :                          __func__, rn->p.family);
    1191           0 :                 goto failure;
    1192             :         }
    1193             : 
    1194             :         /*
    1195             :          * What we matched against
    1196             :          */
    1197           1 :         stream_putw(s, rnh->resolved_route.family);
    1198           1 :         stream_putc(s, rnh->resolved_route.prefixlen);
    1199           1 :         switch (rnh->resolved_route.family) {
    1200           0 :         case AF_INET:
    1201           0 :                 stream_put_in_addr(s, &rnh->resolved_route.u.prefix4);
    1202           0 :                 break;
    1203           1 :         case AF_INET6:
    1204           1 :                 stream_put(s, &rnh->resolved_route.u.prefix6, IPV6_MAX_BYTELEN);
    1205           1 :                 break;
    1206           0 :         default:
    1207           0 :                 flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY,
    1208             :                          "%s: Unknown family (%d) notification attempted",
    1209             :                          __func__, rn->p.family);
    1210           0 :                 goto failure;
    1211             :         }
    1212             : 
    1213           1 :         if (srte_color)
    1214           0 :                 stream_putl(s, srte_color);
    1215             : 
    1216           1 :         if (re) {
    1217           1 :                 struct zapi_nexthop znh;
    1218           1 :                 struct nexthop_group *nhg;
    1219             : 
    1220           1 :                 stream_putc(s, re->type);
    1221           1 :                 stream_putw(s, re->instance);
    1222           1 :                 stream_putc(s, re->distance);
    1223           1 :                 stream_putl(s, re->metric);
    1224           1 :                 num = 0;
    1225           1 :                 nump = stream_get_endp(s);
    1226           1 :                 stream_putc(s, 0);
    1227             : 
    1228           1 :                 nhg = rib_get_fib_nhg(re);
    1229           2 :                 for (ALL_NEXTHOPS_PTR(nhg, nh))
    1230           2 :                         if (rnh_nexthop_valid(re, nh)) {
    1231           1 :                                 zapi_nexthop_from_nexthop(&znh, nh);
    1232           1 :                                 ret = zapi_nexthop_encode(s, &znh, 0, message);
    1233           1 :                                 if (ret < 0)
    1234           0 :                                         goto failure;
    1235             : 
    1236           1 :                                 num++;
    1237             :                         }
    1238             : 
    1239           1 :                 nhg = rib_get_fib_backup_nhg(re);
    1240           1 :                 if (nhg) {
    1241           1 :                         for (ALL_NEXTHOPS_PTR(nhg, nh))
    1242           0 :                                 if (rnh_nexthop_valid(re, nh)) {
    1243           0 :                                         zapi_nexthop_from_nexthop(&znh, nh);
    1244           0 :                                         ret = zapi_nexthop_encode(
    1245             :                                                 s, &znh, 0 /* flags */,
    1246             :                                                 0 /* message */);
    1247           0 :                                         if (ret < 0)
    1248           0 :                                                 goto failure;
    1249             : 
    1250           0 :                                         num++;
    1251             :                                 }
    1252             :                 }
    1253             : 
    1254           1 :                 stream_putc_at(s, nump, num);
    1255             :         } else {
    1256           0 :                 stream_putc(s, 0); // type
    1257           0 :                 stream_putw(s, 0); // instance
    1258           0 :                 stream_putc(s, 0); // distance
    1259           0 :                 stream_putl(s, 0); // metric
    1260           0 :                 stream_putc(s, 0); // nexthops
    1261             :         }
    1262           1 :         stream_putw_at(s, 0, stream_get_endp(s));
    1263             : 
    1264           1 :         client->nh_last_upd_time = monotime(NULL);
    1265           1 :         return zserv_send_message(client, s);
    1266             : 
    1267           0 : failure:
    1268             : 
    1269           0 :         stream_free(s);
    1270           0 :         return -1;
    1271             : }
    1272             : 
    1273             : 
    1274             : /*
    1275             :  * Render a nexthop into a json object; the caller allocates and owns
    1276             :  * the json object memory.
    1277             :  */
    1278           0 : void show_nexthop_json_helper(json_object *json_nexthop,
    1279             :                               const struct nexthop *nexthop,
    1280             :                               const struct route_entry *re)
    1281             : {
    1282           0 :         json_object *json_labels = NULL;
    1283           0 :         json_object *json_backups = NULL;
    1284           0 :         json_object *json_seg6local = NULL;
    1285           0 :         json_object *json_seg6 = NULL;
    1286           0 :         int i;
    1287             : 
    1288           0 :         json_object_int_add(json_nexthop, "flags", nexthop->flags);
    1289             : 
    1290           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
    1291           0 :                 json_object_boolean_true_add(json_nexthop, "duplicate");
    1292             : 
    1293           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
    1294           0 :                 json_object_boolean_true_add(json_nexthop, "fib");
    1295             : 
    1296           0 :         switch (nexthop->type) {
    1297           0 :         case NEXTHOP_TYPE_IPV4:
    1298             :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1299           0 :                 json_object_string_addf(json_nexthop, "ip", "%pI4",
    1300             :                                         &nexthop->gate.ipv4);
    1301           0 :                 json_object_string_add(json_nexthop, "afi", "ipv4");
    1302             : 
    1303           0 :                 if (nexthop->ifindex) {
    1304           0 :                         json_object_int_add(json_nexthop, "interfaceIndex",
    1305             :                                             nexthop->ifindex);
    1306           0 :                         json_object_string_add(json_nexthop, "interfaceName",
    1307           0 :                                                ifindex2ifname(nexthop->ifindex,
    1308           0 :                                                               nexthop->vrf_id));
    1309             :                 }
    1310             :                 break;
    1311           0 :         case NEXTHOP_TYPE_IPV6:
    1312             :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1313           0 :                 json_object_string_addf(json_nexthop, "ip", "%pI6",
    1314             :                                         &nexthop->gate.ipv6);
    1315           0 :                 json_object_string_add(json_nexthop, "afi", "ipv6");
    1316             : 
    1317           0 :                 if (nexthop->ifindex) {
    1318           0 :                         json_object_int_add(json_nexthop, "interfaceIndex",
    1319             :                                             nexthop->ifindex);
    1320           0 :                         json_object_string_add(json_nexthop, "interfaceName",
    1321           0 :                                                ifindex2ifname(nexthop->ifindex,
    1322           0 :                                                               nexthop->vrf_id));
    1323             :                 }
    1324             :                 break;
    1325             : 
    1326           0 :         case NEXTHOP_TYPE_IFINDEX:
    1327           0 :                 json_object_boolean_true_add(json_nexthop, "directlyConnected");
    1328           0 :                 json_object_int_add(json_nexthop, "interfaceIndex",
    1329           0 :                                     nexthop->ifindex);
    1330           0 :                 json_object_string_add(
    1331             :                         json_nexthop, "interfaceName",
    1332           0 :                         ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
    1333           0 :                 break;
    1334           0 :         case NEXTHOP_TYPE_BLACKHOLE:
    1335           0 :                 json_object_boolean_true_add(json_nexthop, "unreachable");
    1336           0 :                 switch (nexthop->bh_type) {
    1337           0 :                 case BLACKHOLE_REJECT:
    1338           0 :                         json_object_boolean_true_add(json_nexthop, "reject");
    1339           0 :                         break;
    1340           0 :                 case BLACKHOLE_ADMINPROHIB:
    1341           0 :                         json_object_boolean_true_add(json_nexthop,
    1342             :                                                      "adminProhibited");
    1343           0 :                         break;
    1344           0 :                 case BLACKHOLE_NULL:
    1345           0 :                         json_object_boolean_true_add(json_nexthop, "blackhole");
    1346           0 :                         break;
    1347             :                 case BLACKHOLE_UNSPEC:
    1348             :                         break;
    1349             :                 }
    1350             :                 break;
    1351             :         }
    1352             : 
    1353             :         /* This nexthop is a resolver for the parent nexthop.
    1354             :          * Set resolver flag for better clarity and delimiter
    1355             :          * in flat list of nexthops in json.
    1356             :          */
    1357           0 :         if (nexthop->rparent)
    1358           0 :                 json_object_boolean_true_add(json_nexthop, "resolver");
    1359             : 
    1360           0 :         if ((re == NULL || (nexthop->vrf_id != re->vrf_id)))
    1361           0 :                 json_object_string_add(json_nexthop, "vrf",
    1362           0 :                                        vrf_id_to_name(nexthop->vrf_id));
    1363             : 
    1364           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
    1365           0 :                 json_object_boolean_true_add(json_nexthop, "duplicate");
    1366             : 
    1367           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
    1368           0 :                 json_object_boolean_true_add(json_nexthop, "active");
    1369             : 
    1370           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
    1371           0 :                 json_object_boolean_true_add(json_nexthop, "onLink");
    1372             : 
    1373           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
    1374           0 :                 json_object_boolean_true_add(json_nexthop, "linkDown");
    1375             : 
    1376           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
    1377           0 :                 json_object_boolean_true_add(json_nexthop, "recursive");
    1378             : 
    1379           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
    1380           0 :                 json_backups = json_object_new_array();
    1381           0 :                 for (i = 0; i < nexthop->backup_num; i++) {
    1382           0 :                         json_object_array_add(
    1383             :                                 json_backups,
    1384           0 :                                 json_object_new_int(nexthop->backup_idx[i]));
    1385             :                 }
    1386             : 
    1387           0 :                 json_object_object_add(json_nexthop, "backupIndex",
    1388             :                                        json_backups);
    1389             :         }
    1390             : 
    1391           0 :         switch (nexthop->type) {
    1392           0 :         case NEXTHOP_TYPE_IPV4:
    1393             :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1394           0 :                 if (nexthop->src.ipv4.s_addr)
    1395           0 :                         json_object_string_addf(json_nexthop, "source", "%pI4",
    1396             :                                                 &nexthop->src.ipv4);
    1397             :                 break;
    1398           0 :         case NEXTHOP_TYPE_IPV6:
    1399             :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1400           0 :                 if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
    1401           0 :                         json_object_string_addf(json_nexthop, "source", "%pI6",
    1402             :                                                 &nexthop->src.ipv6);
    1403             :                 break;
    1404             :         case NEXTHOP_TYPE_IFINDEX:
    1405             :         case NEXTHOP_TYPE_BLACKHOLE:
    1406             :                 break;
    1407             :         }
    1408             : 
    1409           0 :         if (nexthop->nh_label && nexthop->nh_label->num_labels) {
    1410           0 :                 json_labels = json_object_new_array();
    1411             : 
    1412           0 :                 for (int label_index = 0;
    1413           0 :                      label_index < nexthop->nh_label->num_labels; label_index++)
    1414           0 :                         json_object_array_add(
    1415             :                                 json_labels,
    1416             :                                 json_object_new_int(
    1417           0 :                                         nexthop->nh_label->label[label_index]));
    1418             : 
    1419           0 :                 json_object_object_add(json_nexthop, "labels", json_labels);
    1420             :         }
    1421             : 
    1422           0 :         if (nexthop->weight)
    1423           0 :                 json_object_int_add(json_nexthop, "weight", nexthop->weight);
    1424             : 
    1425           0 :         if (nexthop->srte_color)
    1426           0 :                 json_object_int_add(json_nexthop, "srteColor",
    1427             :                                     nexthop->srte_color);
    1428             : 
    1429           0 :         if (nexthop->nh_srv6) {
    1430           0 :                 json_seg6local = json_object_new_object();
    1431           0 :                 json_object_string_add(
    1432             :                         json_seg6local, "action",
    1433             :                         seg6local_action2str(
    1434           0 :                                 nexthop->nh_srv6->seg6local_action));
    1435           0 :                 json_object_object_add(json_nexthop, "seg6local",
    1436             :                                        json_seg6local);
    1437             : 
    1438           0 :                 json_seg6 = json_object_new_object();
    1439           0 :                 json_object_string_addf(json_seg6, "segs", "%pI6",
    1440           0 :                                         &nexthop->nh_srv6->seg6_segs);
    1441           0 :                 json_object_object_add(json_nexthop, "seg6", json_seg6);
    1442             :         }
    1443           0 : }
    1444             : 
    1445             : /*
    1446             :  * Helper for nexthop output, used in the 'show ip route' path
    1447             :  */
    1448           0 : void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
    1449             :                                const struct nexthop *nexthop)
    1450             : {
    1451           0 :         char buf[MPLS_LABEL_STRLEN];
    1452           0 :         int i;
    1453             : 
    1454           0 :         switch (nexthop->type) {
    1455           0 :         case NEXTHOP_TYPE_IPV4:
    1456             :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1457           0 :                 vty_out(vty, " via %pI4", &nexthop->gate.ipv4);
    1458           0 :                 if (nexthop->ifindex)
    1459           0 :                         vty_out(vty, ", %s",
    1460             :                                 ifindex2ifname(nexthop->ifindex,
    1461           0 :                                                nexthop->vrf_id));
    1462             :                 break;
    1463           0 :         case NEXTHOP_TYPE_IPV6:
    1464             :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1465           0 :                 vty_out(vty, " via %s",
    1466           0 :                         inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
    1467             :                                   sizeof(buf)));
    1468           0 :                 if (nexthop->ifindex)
    1469           0 :                         vty_out(vty, ", %s",
    1470             :                                 ifindex2ifname(nexthop->ifindex,
    1471           0 :                                                nexthop->vrf_id));
    1472             :                 break;
    1473             : 
    1474           0 :         case NEXTHOP_TYPE_IFINDEX:
    1475           0 :                 vty_out(vty, " is directly connected, %s",
    1476           0 :                         ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
    1477           0 :                 break;
    1478           0 :         case NEXTHOP_TYPE_BLACKHOLE:
    1479           0 :                 vty_out(vty, " unreachable");
    1480           0 :                 switch (nexthop->bh_type) {
    1481           0 :                 case BLACKHOLE_REJECT:
    1482           0 :                         vty_out(vty, " (ICMP unreachable)");
    1483           0 :                         break;
    1484           0 :                 case BLACKHOLE_ADMINPROHIB:
    1485           0 :                         vty_out(vty, " (ICMP admin-prohibited)");
    1486           0 :                         break;
    1487           0 :                 case BLACKHOLE_NULL:
    1488           0 :                         vty_out(vty, " (blackhole)");
    1489           0 :                         break;
    1490             :                 case BLACKHOLE_UNSPEC:
    1491             :                         break;
    1492             :                 }
    1493             :                 break;
    1494             :         }
    1495             : 
    1496           0 :         if ((re == NULL || (nexthop->vrf_id != re->vrf_id)))
    1497           0 :                 vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id));
    1498             : 
    1499           0 :         if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
    1500           0 :                 vty_out(vty, " inactive");
    1501             : 
    1502           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
    1503           0 :                 vty_out(vty, " onlink");
    1504             : 
    1505           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
    1506           0 :                 vty_out(vty, " linkdown");
    1507             : 
    1508           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
    1509           0 :                 vty_out(vty, " (recursive)");
    1510             : 
    1511           0 :         switch (nexthop->type) {
    1512           0 :         case NEXTHOP_TYPE_IPV4:
    1513             :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1514           0 :                 if (nexthop->src.ipv4.s_addr) {
    1515           0 :                         vty_out(vty, ", src %pI4", &nexthop->src.ipv4);
    1516             :                         /* SR-TE information */
    1517           0 :                         if (nexthop->srte_color)
    1518           0 :                                 vty_out(vty, ", SR-TE color %u",
    1519             :                                         nexthop->srte_color);
    1520             :                 }
    1521             :                 break;
    1522           0 :         case NEXTHOP_TYPE_IPV6:
    1523             :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1524           0 :                 if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
    1525           0 :                         vty_out(vty, ", src %pI6", &nexthop->src.ipv6);
    1526             :                 break;
    1527             :         case NEXTHOP_TYPE_IFINDEX:
    1528             :         case NEXTHOP_TYPE_BLACKHOLE:
    1529             :                 break;
    1530             :         }
    1531             : 
    1532             :         /* Label information */
    1533           0 :         if (nexthop->nh_label && nexthop->nh_label->num_labels) {
    1534           0 :                 vty_out(vty, ", label %s",
    1535             :                         mpls_label2str(nexthop->nh_label->num_labels,
    1536           0 :                                        nexthop->nh_label->label, buf,
    1537             :                                        sizeof(buf), 1));
    1538             :         }
    1539             : 
    1540           0 :         if (nexthop->nh_srv6) {
    1541           0 :                 seg6local_context2str(buf, sizeof(buf),
    1542           0 :                                       &nexthop->nh_srv6->seg6local_ctx,
    1543           0 :                                       nexthop->nh_srv6->seg6local_action);
    1544           0 :                 vty_out(vty, ", seg6local %s %s",
    1545             :                         seg6local_action2str(
    1546           0 :                                 nexthop->nh_srv6->seg6local_action),
    1547             :                         buf);
    1548           0 :                 vty_out(vty, ", seg6 %pI6", &nexthop->nh_srv6->seg6_segs);
    1549             :         }
    1550             : 
    1551           0 :         if (nexthop->weight)
    1552           0 :                 vty_out(vty, ", weight %u", nexthop->weight);
    1553             : 
    1554           0 :         if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
    1555           0 :                 vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
    1556             : 
    1557           0 :                 for (i = 1; i < nexthop->backup_num; i++)
    1558           0 :                         vty_out(vty, ",%d", nexthop->backup_idx[i]);
    1559             :         }
    1560           0 : }
    1561             : 
    1562           0 : static void print_rnh(struct route_node *rn, struct vty *vty, json_object *json)
    1563             : {
    1564           0 :         struct rnh *rnh;
    1565           0 :         struct nexthop *nexthop;
    1566           0 :         struct listnode *node;
    1567           0 :         struct zserv *client;
    1568           0 :         char buf[BUFSIZ];
    1569           0 :         json_object *json_nht = NULL;
    1570           0 :         json_object *json_client_array = NULL;
    1571           0 :         json_object *json_client = NULL;
    1572           0 :         json_object *json_nexthop_array = NULL;
    1573           0 :         json_object *json_nexthop = NULL;
    1574             : 
    1575           0 :         rnh = rn->info;
    1576             : 
    1577           0 :         if (json) {
    1578           0 :                 json_nht = json_object_new_object();
    1579           0 :                 json_nexthop_array = json_object_new_array();
    1580           0 :                 json_client_array = json_object_new_array();
    1581             : 
    1582           0 :                 json_object_object_add(
    1583             :                         json,
    1584           0 :                         inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
    1585             :                         json_nht);
    1586           0 :                 json_object_boolean_add(
    1587             :                         json_nht, "nhtConnected",
    1588           0 :                         CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED));
    1589           0 :                 json_object_object_add(json_nht, "clientList",
    1590             :                                        json_client_array);
    1591           0 :                 json_object_object_add(json_nht, "nexthops",
    1592             :                                        json_nexthop_array);
    1593             :         } else {
    1594           0 :                 vty_out(vty, "%s%s\n",
    1595           0 :                         inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
    1596           0 :                         CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)
    1597             :                                 ? "(Connected)"
    1598             :                                 : "");
    1599             :         }
    1600             : 
    1601           0 :         if (rnh->state) {
    1602           0 :                 if (json)
    1603           0 :                         json_object_string_add(
    1604             :                                 json_nht, "resolvedProtocol",
    1605           0 :                                 zebra_route_string(rnh->state->type));
    1606             :                 else
    1607           0 :                         vty_out(vty, " resolved via %s\n",
    1608           0 :                                 zebra_route_string(rnh->state->type));
    1609             : 
    1610           0 :                 for (nexthop = rnh->state->nhe->nhg.nexthop; nexthop;
    1611           0 :                      nexthop = nexthop->next) {
    1612           0 :                         if (json) {
    1613           0 :                                 json_nexthop = json_object_new_object();
    1614           0 :                                 json_object_array_add(json_nexthop_array,
    1615             :                                                       json_nexthop);
    1616           0 :                                 show_nexthop_json_helper(json_nexthop, nexthop,
    1617             :                                                          NULL);
    1618             :                         } else {
    1619           0 :                                 show_route_nexthop_helper(vty, NULL, nexthop);
    1620           0 :                                 vty_out(vty, "\n");
    1621             :                         }
    1622             :                 }
    1623             :         } else {
    1624           0 :                 if (json)
    1625           0 :                         json_object_boolean_add(
    1626             :                                 json_nht, "unresolved",
    1627           0 :                                 CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED));
    1628             :                 else
    1629           0 :                         vty_out(vty, " unresolved%s\n",
    1630           0 :                                 CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)
    1631             :                                         ? "(Connected)"
    1632             :                                         : "");
    1633             :         }
    1634             : 
    1635           0 :         if (!json)
    1636           0 :                 vty_out(vty, " Client list:");
    1637             : 
    1638           0 :         for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
    1639           0 :                 if (json) {
    1640           0 :                         json_client = json_object_new_object();
    1641           0 :                         json_object_array_add(json_client_array, json_client);
    1642             : 
    1643           0 :                         json_object_string_add(
    1644             :                                 json_client, "protocol",
    1645           0 :                                 zebra_route_string(client->proto));
    1646           0 :                         json_object_int_add(json_client, "socket",
    1647           0 :                                             client->sock);
    1648           0 :                         json_object_string_add(json_client, "protocolFiltered",
    1649           0 :                                                (rnh->filtered[client->proto]
    1650             :                                                         ? "(filtered)"
    1651             :                                                         : "none"));
    1652             :                 } else {
    1653           0 :                         vty_out(vty, " %s(fd %d)%s",
    1654             :                                 zebra_route_string(client->proto), client->sock,
    1655           0 :                                 rnh->filtered[client->proto] ? "(filtered)"
    1656             :                                                              : "");
    1657             :                 }
    1658             :         }
    1659             : 
    1660           0 :         if (!list_isempty(rnh->zebra_pseudowire_list)) {
    1661           0 :                 if (json)
    1662           0 :                         json_object_boolean_true_add(json_nht,
    1663             :                                                      "zebraPseudowires");
    1664             :                 else
    1665           0 :                         vty_out(vty, " zebra[pseudowires]");
    1666             :         }
    1667             : 
    1668           0 :         if (!json)
    1669           0 :                 vty_out(vty, "\n");
    1670           0 : }
    1671             : 
    1672           8 : static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, afi_t afi, safi_t safi,
    1673             :                                     struct zserv *client)
    1674             : {
    1675           8 :         struct route_table *ntable;
    1676           8 :         struct route_node *nrn;
    1677           8 :         struct rnh *rnh;
    1678             : 
    1679           8 :         if (IS_ZEBRA_DEBUG_NHT) {
    1680           8 :                 struct vrf *vrf = vrf_lookup_by_id(vrf_id);
    1681             : 
    1682           8 :                 zlog_debug("%s(%u): Client %s RNH cleanup for family %s",
    1683             :                            VRF_LOGNAME(vrf), vrf_id,
    1684             :                            zebra_route_string(client->proto), afi2str(afi));
    1685             :         }
    1686             : 
    1687           8 :         ntable = get_rnh_table(vrf_id, afi, safi);
    1688           8 :         if (!ntable) {
    1689           0 :                 zlog_debug("cleanup_rnh_client: rnh table not found");
    1690           0 :                 return -1;
    1691             :         }
    1692             : 
    1693           8 :         for (nrn = route_top(ntable); nrn; nrn = route_next(nrn)) {
    1694           0 :                 if (!nrn->info)
    1695           0 :                         continue;
    1696             : 
    1697           0 :                 rnh = nrn->info;
    1698           0 :                 zebra_remove_rnh_client(rnh, client);
    1699             :         }
    1700             :         return 1;
    1701             : }
    1702             : 
    1703             : /* Cleanup registered nexthops (across VRFs) upon client disconnect. */
    1704           2 : static int zebra_client_cleanup_rnh(struct zserv *client)
    1705             : {
    1706           2 :         struct vrf *vrf;
    1707           2 :         struct zebra_vrf *zvrf;
    1708             : 
    1709           6 :         RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
    1710           2 :                 zvrf = vrf->info;
    1711           2 :                 if (zvrf) {
    1712           4 :                         zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP,
    1713             :                                                  SAFI_UNICAST, client);
    1714           4 :                         zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP,
    1715             :                                                  SAFI_MULTICAST, client);
    1716           4 :                         zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP6,
    1717             :                                                  SAFI_UNICAST, client);
    1718           4 :                         zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP6,
    1719             :                                                  SAFI_MULTICAST, client);
    1720             :                 }
    1721             :         }
    1722             : 
    1723           2 :         return 0;
    1724             : }
    1725             : 
    1726           0 : int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family)
    1727             : {
    1728           0 :         if (((family == AF_INET) && zvrf->zebra_rnh_ip_default_route)
    1729           0 :             || ((family == AF_INET6) && zvrf->zebra_rnh_ipv6_default_route))
    1730             :                 return 1;
    1731             :         else
    1732           0 :                 return 0;
    1733             : }
    1734             : 
    1735             : /*
    1736             :  * UI control to avoid notifications if backup nexthop status changes
    1737             :  */
    1738           0 : void rnh_set_hide_backups(bool hide_p)
    1739             : {
    1740           0 :         rnh_hide_backups = hide_p;
    1741           0 : }
    1742             : 
    1743           0 : bool rnh_get_hide_backups(void)
    1744             : {
    1745           0 :         return rnh_hide_backups;
    1746             : }

Generated by: LCOV version v1.16-topotato