Coverage Report

Created: 2025-08-26 06:20

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