Coverage Report

Created: 2025-08-28 06:29

/src/frr/pimd/pim_nht.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * PIM for Quagga
4
 * Copyright (C) 2017 Cumulus Networks, Inc.
5
 * Chirag Shah
6
 */
7
#include <zebra.h>
8
#include "network.h"
9
#include "zclient.h"
10
#include "stream.h"
11
#include "nexthop.h"
12
#include "if.h"
13
#include "hash.h"
14
#include "jhash.h"
15
16
#include "lib/printfrr.h"
17
18
#include "pimd.h"
19
#include "pimd/pim_nht.h"
20
#include "pim_instance.h"
21
#include "log.h"
22
#include "pim_time.h"
23
#include "pim_oil.h"
24
#include "pim_ifchannel.h"
25
#include "pim_mroute.h"
26
#include "pim_zebra.h"
27
#include "pim_upstream.h"
28
#include "pim_join.h"
29
#include "pim_jp_agg.h"
30
#include "pim_zebra.h"
31
#include "pim_zlookup.h"
32
#include "pim_rp.h"
33
#include "pim_addr.h"
34
35
/**
36
 * pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
37
 *   command to Zebra.
38
 */
39
void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient,
40
         struct pim_nexthop_cache *pnc, int command)
41
66.8k
{
42
66.8k
  struct prefix p;
43
66.8k
  int ret;
44
45
66.8k
  pim_addr_to_prefix(&p, pnc->rpf.rpf_addr);
46
66.8k
  ret = zclient_send_rnh(zclient, command, &p, SAFI_UNICAST, false, false,
47
66.8k
             pim->vrf->vrf_id);
48
66.8k
  if (ret == ZCLIENT_SEND_FAILURE)
49
66.8k
    zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
50
51
66.8k
  if (PIM_DEBUG_PIM_NHT)
52
0
    zlog_debug(
53
66.8k
      "%s: NHT %sregistered addr %pFX(%s) with Zebra ret:%d ",
54
66.8k
      __func__,
55
66.8k
      (command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", &p,
56
66.8k
      pim->vrf->name, ret);
57
58
66.8k
  return;
59
66.8k
}
60
61
struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim,
62
             struct pim_rpf *rpf)
63
324k
{
64
324k
  struct pim_nexthop_cache *pnc = NULL;
65
324k
  struct pim_nexthop_cache lookup;
66
67
324k
  lookup.rpf.rpf_addr = rpf->rpf_addr;
68
324k
  pnc = hash_lookup(pim->rpf_hash, &lookup);
69
70
324k
  return pnc;
71
324k
}
72
73
static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim,
74
                   struct pim_rpf *rpf_addr)
75
33.5k
{
76
33.5k
  struct pim_nexthop_cache *pnc;
77
33.5k
  char hash_name[64];
78
79
33.5k
  pnc = XCALLOC(MTYPE_PIM_NEXTHOP_CACHE,
80
33.5k
          sizeof(struct pim_nexthop_cache));
81
33.5k
  pnc->rpf.rpf_addr = rpf_addr->rpf_addr;
82
83
33.5k
  pnc = hash_get(pim->rpf_hash, pnc, hash_alloc_intern);
84
85
33.5k
  pnc->rp_list = list_new();
86
33.5k
  pnc->rp_list->cmp = pim_rp_list_cmp;
87
88
33.5k
  snprintfrr(hash_name, sizeof(hash_name), "PNC %pPA(%s) Upstream Hash",
89
33.5k
       &pnc->rpf.rpf_addr, pim->vrf->name);
90
33.5k
  pnc->upstream_hash = hash_create_size(8192, pim_upstream_hash_key,
91
33.5k
                pim_upstream_equal, hash_name);
92
93
33.5k
  return pnc;
94
33.5k
}
95
96
static struct pim_nexthop_cache *pim_nht_get(struct pim_instance *pim,
97
               pim_addr addr)
98
167k
{
99
167k
  struct pim_nexthop_cache *pnc = NULL;
100
167k
  struct pim_rpf rpf;
101
167k
  struct zclient *zclient = NULL;
102
103
167k
  zclient = pim_zebra_zclient_get();
104
167k
  memset(&rpf, 0, sizeof(rpf));
105
167k
  rpf.rpf_addr = addr;
106
107
167k
  pnc = pim_nexthop_cache_find(pim, &rpf);
108
167k
  if (!pnc) {
109
33.5k
    pnc = pim_nexthop_cache_add(pim, &rpf);
110
33.5k
    pim_sendmsg_zebra_rnh(pim, zclient, pnc,
111
33.5k
              ZEBRA_NEXTHOP_REGISTER);
112
33.5k
    if (PIM_DEBUG_PIM_NHT_DETAIL)
113
0
      zlog_debug(
114
33.5k
        "%s: NHT cache and zebra notification added for %pPA(%s)",
115
33.5k
        __func__, &addr, pim->vrf->name);
116
33.5k
  }
117
118
167k
  return pnc;
119
167k
}
120
121
/* TBD: this does several distinct things and should probably be split up.
122
 * (checking state vs. returning pnc vs. adding upstream vs. adding rp)
123
 */
124
int pim_find_or_track_nexthop(struct pim_instance *pim, pim_addr addr,
125
            struct pim_upstream *up, struct rp_info *rp,
126
            struct pim_nexthop_cache *out_pnc)
127
167k
{
128
167k
  struct pim_nexthop_cache *pnc;
129
167k
  struct listnode *ch_node = NULL;
130
131
167k
  pnc = pim_nht_get(pim, addr);
132
133
167k
  assertf(up || rp, "addr=%pPA", &addr);
134
135
167k
  if (rp != NULL) {
136
64.9k
    ch_node = listnode_lookup(pnc->rp_list, rp);
137
64.9k
    if (ch_node == NULL)
138
35.4k
      listnode_add_sort(pnc->rp_list, rp);
139
64.9k
  }
140
141
167k
  if (up != NULL)
142
102k
    (void)hash_get(pnc->upstream_hash, up, hash_alloc_intern);
143
144
167k
  if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID)) {
145
0
    if (out_pnc)
146
0
      memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache));
147
0
    return 1;
148
0
  }
149
150
167k
  return 0;
151
167k
}
152
153
void pim_nht_bsr_add(struct pim_instance *pim, pim_addr addr)
154
8
{
155
8
  struct pim_nexthop_cache *pnc;
156
157
8
  pnc = pim_nht_get(pim, addr);
158
159
8
  pnc->bsr_count++;
160
8
}
161
162
static void pim_nht_drop_maybe(struct pim_instance *pim,
163
             struct pim_nexthop_cache *pnc)
164
78.0k
{
165
78.0k
  if (PIM_DEBUG_PIM_NHT)
166
0
    zlog_debug(
167
78.0k
      "%s: NHT %pPA(%s) rp_list count:%d upstream count:%ld BSR count:%u",
168
78.0k
      __func__, &pnc->rpf.rpf_addr, pim->vrf->name,
169
78.0k
      pnc->rp_list->count, pnc->upstream_hash->count,
170
78.0k
      pnc->bsr_count);
171
172
78.0k
  if (pnc->rp_list->count == 0 && pnc->upstream_hash->count == 0
173
78.0k
      && pnc->bsr_count == 0) {
174
33.2k
    struct zclient *zclient = pim_zebra_zclient_get();
175
176
33.2k
    pim_sendmsg_zebra_rnh(pim, zclient, pnc,
177
33.2k
              ZEBRA_NEXTHOP_UNREGISTER);
178
179
33.2k
    list_delete(&pnc->rp_list);
180
33.2k
    hash_free(pnc->upstream_hash);
181
182
33.2k
    hash_release(pim->rpf_hash, pnc);
183
33.2k
    if (pnc->nexthop)
184
0
      nexthops_free(pnc->nexthop);
185
33.2k
    XFREE(MTYPE_PIM_NEXTHOP_CACHE, pnc);
186
33.2k
  }
187
78.0k
}
188
189
void pim_delete_tracked_nexthop(struct pim_instance *pim, pim_addr addr,
190
        struct pim_upstream *up, struct rp_info *rp)
191
108k
{
192
108k
  struct pim_nexthop_cache *pnc = NULL;
193
108k
  struct pim_nexthop_cache lookup;
194
108k
  struct pim_upstream *upstream = NULL;
195
196
  /* Remove from RPF hash if it is the last entry */
197
108k
  lookup.rpf.rpf_addr = addr;
198
108k
  pnc = hash_lookup(pim->rpf_hash, &lookup);
199
108k
  if (!pnc) {
200
29.9k
    zlog_warn("attempting to delete nonexistent NHT entry %pPA",
201
29.9k
        &addr);
202
29.9k
    return;
203
29.9k
  }
204
205
78.0k
  if (rp) {
206
    /* Release the (*, G)upstream from pnc->upstream_hash,
207
     * whose Group belongs to the RP getting deleted
208
     */
209
28.3M
    frr_each (rb_pim_upstream, &pim->upstream_head, upstream) {
210
28.3M
      struct prefix grp;
211
28.3M
      struct rp_info *trp_info;
212
213
28.3M
      if (!pim_addr_is_any(upstream->sg.src))
214
21.1M
        continue;
215
216
7.17M
      pim_addr_to_prefix(&grp, upstream->sg.grp);
217
7.17M
      trp_info = pim_rp_find_match_group(pim, &grp);
218
7.17M
      if (trp_info == rp)
219
54.6k
        hash_release(pnc->upstream_hash, upstream);
220
7.17M
    }
221
35.2k
    listnode_delete(pnc->rp_list, rp);
222
35.2k
  }
223
224
78.0k
  if (up)
225
42.7k
    hash_release(pnc->upstream_hash, up);
226
227
78.0k
  pim_nht_drop_maybe(pim, pnc);
228
78.0k
}
229
230
void pim_nht_bsr_del(struct pim_instance *pim, pim_addr addr)
231
8
{
232
8
  struct pim_nexthop_cache *pnc = NULL;
233
8
  struct pim_nexthop_cache lookup;
234
235
  /*
236
   * Nothing to do here if the address to unregister
237
   * is 0.0.0.0 as that the BSR has not been registered
238
   * for tracking yet.
239
   */
240
8
  if (pim_addr_is_any(addr))
241
1
    return;
242
243
7
  lookup.rpf.rpf_addr = addr;
244
245
7
  pnc = hash_lookup(pim->rpf_hash, &lookup);
246
247
7
  if (!pnc) {
248
0
    zlog_warn("attempting to delete nonexistent NHT BSR entry %pPA",
249
0
        &addr);
250
0
    return;
251
0
  }
252
253
7
  assertf(pnc->bsr_count > 0, "addr=%pPA", &addr);
254
7
  pnc->bsr_count--;
255
256
7
  pim_nht_drop_maybe(pim, pnc);
257
7
}
258
259
bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr,
260
         struct interface *src_ifp, pim_addr src_ip)
261
2
{
262
2
  struct pim_nexthop_cache *pnc = NULL;
263
2
  struct pim_nexthop_cache lookup;
264
2
  struct pim_neighbor *nbr = NULL;
265
2
  struct nexthop *nh;
266
2
  struct interface *ifp;
267
268
2
  lookup.rpf.rpf_addr = bsr_addr;
269
270
2
  pnc = hash_lookup(pim->rpf_hash, &lookup);
271
2
  if (!pnc || !CHECK_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED)) {
272
    /* BSM from a new freshly registered BSR - do a synchronous
273
     * zebra query since otherwise we'd drop the first packet,
274
     * leading to additional delay in picking up BSM data
275
     */
276
277
    /* FIXME: this should really be moved into a generic NHT
278
     * function that does "add and get immediate result" or maybe
279
     * "check cache or get immediate result." But until that can
280
     * be worked in, here's a copy of the code below :(
281
     */
282
2
    struct pim_zlookup_nexthop nexthop_tab[router->multipath];
283
2
    ifindex_t i;
284
2
    struct interface *ifp = NULL;
285
2
    int num_ifindex;
286
287
2
    memset(nexthop_tab, 0, sizeof(nexthop_tab));
288
2
    num_ifindex = zclient_lookup_nexthop(
289
2
      pim, nexthop_tab, router->multipath, bsr_addr,
290
2
      PIM_NEXTHOP_LOOKUP_MAX);
291
292
2
    if (num_ifindex <= 0)
293
2
      return false;
294
295
0
    for (i = 0; i < num_ifindex; i++) {
296
0
      struct pim_zlookup_nexthop *znh = &nexthop_tab[i];
297
298
      /* pim_zlookup_nexthop has no ->type */
299
300
      /* 1:1 match code below with znh instead of nh */
301
0
      ifp = if_lookup_by_index(znh->ifindex,
302
0
             pim->vrf->vrf_id);
303
304
0
      if (!ifp || !ifp->info)
305
0
        continue;
306
307
0
      if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
308
0
        return true;
309
310
0
      nbr = pim_neighbor_find(ifp, znh->nexthop_addr, true);
311
0
      if (!nbr)
312
0
        continue;
313
314
0
      return znh->ifindex == src_ifp->ifindex;
315
0
    }
316
0
    return false;
317
0
  }
318
319
0
  if (!CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID))
320
0
    return false;
321
322
  /* if we accept BSMs from more than one ECMP nexthop, this will cause
323
   * BSM message "multiplication" for each ECMP hop.  i.e. if you have
324
   * 4-way ECMP and 4 hops you end up with 256 copies of each BSM
325
   * message.
326
   *
327
   * so...  only accept the first (IPv4) valid nexthop as source.
328
   */
329
330
0
  for (nh = pnc->nexthop; nh; nh = nh->next) {
331
0
    pim_addr nhaddr;
332
333
0
    switch (nh->type) {
334
0
#if PIM_IPV == 4
335
0
    case NEXTHOP_TYPE_IPV4:
336
0
      if (nh->ifindex == IFINDEX_INTERNAL)
337
0
        continue;
338
339
      /* fallthru */
340
0
    case NEXTHOP_TYPE_IPV4_IFINDEX:
341
0
      nhaddr = nh->gate.ipv4;
342
0
      break;
343
0
    case NEXTHOP_TYPE_IPV6:
344
0
    case NEXTHOP_TYPE_IPV6_IFINDEX:
345
0
      continue;
346
#else
347
    case NEXTHOP_TYPE_IPV6:
348
      if (nh->ifindex == IFINDEX_INTERNAL)
349
        continue;
350
351
      /* fallthru */
352
    case NEXTHOP_TYPE_IPV6_IFINDEX:
353
      nhaddr = nh->gate.ipv6;
354
      break;
355
    case NEXTHOP_TYPE_IPV4:
356
    case NEXTHOP_TYPE_IPV4_IFINDEX:
357
      continue;
358
#endif
359
0
    case NEXTHOP_TYPE_IFINDEX:
360
0
      nhaddr = bsr_addr;
361
0
      break;
362
363
0
    case NEXTHOP_TYPE_BLACKHOLE:
364
0
      continue;
365
0
    }
366
367
0
    ifp = if_lookup_by_index(nh->ifindex, pim->vrf->vrf_id);
368
0
    if (!ifp || !ifp->info)
369
0
      continue;
370
371
0
    if (if_is_loopback(ifp) && if_is_loopback(src_ifp))
372
0
      return true;
373
374
    /* MRIB (IGP) may be pointing at a router where PIM is down */
375
376
0
    nbr = pim_neighbor_find(ifp, nhaddr, true);
377
378
0
    if (!nbr)
379
0
      continue;
380
381
0
    return nh->ifindex == src_ifp->ifindex;
382
0
  }
383
0
  return false;
384
0
}
385
386
void pim_rp_nexthop_del(struct rp_info *rp_info)
387
26.1k
{
388
26.1k
  rp_info->rp.source_nexthop.interface = NULL;
389
26.1k
  rp_info->rp.source_nexthop.mrib_nexthop_addr = PIMADDR_ANY;
390
26.1k
  rp_info->rp.source_nexthop.mrib_metric_preference =
391
26.1k
    router->infinite_assert_metric.metric_preference;
392
26.1k
  rp_info->rp.source_nexthop.mrib_route_metric =
393
26.1k
    router->infinite_assert_metric.route_metric;
394
26.1k
}
395
396
/* Update RP nexthop info based on Nexthop update received from Zebra.*/
397
static void pim_update_rp_nh(struct pim_instance *pim,
398
           struct pim_nexthop_cache *pnc)
399
0
{
400
0
  struct listnode *node = NULL;
401
0
  struct rp_info *rp_info = NULL;
402
403
  /*Traverse RP list and update each RP Nexthop info */
404
0
  for (ALL_LIST_ELEMENTS_RO(pnc->rp_list, node, rp_info)) {
405
0
    if (pim_rpf_addr_is_inaddr_any(&rp_info->rp))
406
0
      continue;
407
408
    // Compute PIM RPF using cached nexthop
409
0
    if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop,
410
0
               rp_info->rp.rpf_addr,
411
0
               &rp_info->group, 1))
412
0
      pim_rp_nexthop_del(rp_info);
413
0
  }
414
0
}
415
416
/* Update Upstream nexthop info based on Nexthop update received from Zebra.*/
417
static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg)
418
0
{
419
0
  struct pim_instance *pim = (struct pim_instance *)arg;
420
0
  struct pim_upstream *up = (struct pim_upstream *)bucket->data;
421
422
0
  enum pim_rpf_result rpf_result;
423
0
  struct pim_rpf old;
424
425
0
  old.source_nexthop.interface = up->rpf.source_nexthop.interface;
426
0
  rpf_result = pim_rpf_update(pim, up, &old, __func__);
427
428
  /* update kernel multicast forwarding cache (MFC); if the
429
   * RPF nbr is now unreachable the MFC has already been updated
430
   * by pim_rpf_clear
431
   */
432
0
  if (rpf_result == PIM_RPF_CHANGED)
433
0
    pim_upstream_mroute_iif_update(up->channel_oil, __func__);
434
435
0
  if (rpf_result == PIM_RPF_CHANGED ||
436
0
    (rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface))
437
0
    pim_zebra_upstream_rpf_changed(pim, up, &old);
438
439
440
0
  if (PIM_DEBUG_PIM_NHT) {
441
0
    zlog_debug(
442
0
      "%s: NHT upstream %s(%s) old ifp %s new ifp %s",
443
0
      __func__, up->sg_str, pim->vrf->name,
444
0
      old.source_nexthop.interface ? old.source_nexthop
445
0
                     .interface->name
446
0
                 : "Unknown",
447
0
      up->rpf.source_nexthop.interface ? up->rpf.source_nexthop
448
0
                   .interface->name
449
0
               : "Unknown");
450
0
  }
451
452
0
  return HASHWALK_CONTINUE;
453
0
}
454
455
static int pim_update_upstream_nh(struct pim_instance *pim,
456
          struct pim_nexthop_cache *pnc)
457
0
{
458
0
  hash_walk(pnc->upstream_hash, pim_update_upstream_nh_helper, pim);
459
460
0
  pim_zebra_update_all_interfaces(pim);
461
462
0
  return 0;
463
0
}
464
465
static int pim_upstream_nh_if_update_helper(struct hash_bucket *bucket,
466
              void *arg)
467
0
{
468
0
  struct pim_nexthop_cache *pnc = bucket->data;
469
0
  struct pnc_hash_walk_data *pwd = arg;
470
0
  struct pim_instance *pim = pwd->pim;
471
0
  struct interface *ifp = pwd->ifp;
472
0
  struct nexthop *nh_node = NULL;
473
0
  ifindex_t first_ifindex;
474
475
0
  for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) {
476
0
    first_ifindex = nh_node->ifindex;
477
0
    if (ifp != if_lookup_by_index(first_ifindex, pim->vrf->vrf_id))
478
0
      continue;
479
480
0
    if (pnc->upstream_hash->count) {
481
0
      pim_update_upstream_nh(pim, pnc);
482
0
      break;
483
0
    }
484
0
  }
485
486
0
  return HASHWALK_CONTINUE;
487
0
}
488
489
void pim_upstream_nh_if_update(struct pim_instance *pim, struct interface *ifp)
490
0
{
491
0
  struct pnc_hash_walk_data pwd;
492
493
0
  pwd.pim = pim;
494
0
  pwd.ifp = ifp;
495
496
0
  hash_walk(pim->rpf_hash, pim_upstream_nh_if_update_helper, &pwd);
497
0
}
498
499
uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp)
500
0
{
501
0
  uint32_t hash_val;
502
503
0
  if (!src)
504
0
    return 0;
505
506
0
  hash_val = prefix_hash_key(src);
507
0
  if (grp)
508
0
    hash_val ^= prefix_hash_key(grp);
509
0
  return hash_val;
510
0
}
511
512
static int pim_ecmp_nexthop_search(struct pim_instance *pim,
513
           struct pim_nexthop_cache *pnc,
514
           struct pim_nexthop *nexthop, pim_addr src,
515
           struct prefix *grp, int neighbor_needed)
516
0
{
517
0
  struct pim_neighbor *nbrs[router->multipath], *nbr = NULL;
518
0
  struct interface *ifps[router->multipath];
519
0
  struct nexthop *nh_node = NULL;
520
0
  ifindex_t first_ifindex;
521
0
  struct interface *ifp = NULL;
522
0
  uint32_t hash_val = 0, mod_val = 0;
523
0
  uint8_t nh_iter = 0, found = 0;
524
0
  uint32_t i, num_nbrs = 0;
525
0
  struct pim_interface *pim_ifp;
526
527
0
  if (!pnc || !pnc->nexthop_num || !nexthop)
528
0
    return 0;
529
530
0
  pim_addr nh_addr = nexthop->mrib_nexthop_addr;
531
0
  pim_addr grp_addr = pim_addr_from_prefix(grp);
532
533
0
  memset(&nbrs, 0, sizeof(nbrs));
534
0
  memset(&ifps, 0, sizeof(ifps));
535
536
537
  // Current Nexthop is VALID, check to stay on the current path.
538
0
  if (nexthop->interface && nexthop->interface->info &&
539
0
      (!pim_addr_is_any(nh_addr))) {
540
    /* User configured knob to explicitly switch
541
       to new path is disabled or current path
542
       metric is less than nexthop update.
543
     */
544
545
0
    if (pim->ecmp_rebalance_enable == 0) {
546
0
      uint8_t curr_route_valid = 0;
547
      // Check if current nexthop is present in new updated
548
      // Nexthop list.
549
      // If the current nexthop is not valid, candidate to
550
      // choose new Nexthop.
551
0
      for (nh_node = pnc->nexthop; nh_node;
552
0
           nh_node = nh_node->next) {
553
0
        curr_route_valid = (nexthop->interface->ifindex
554
0
                == nh_node->ifindex);
555
0
        if (curr_route_valid)
556
0
          break;
557
0
      }
558
559
0
      if (curr_route_valid &&
560
0
          !pim_if_connected_to_source(nexthop->interface,
561
0
              src)) {
562
0
        nbr = pim_neighbor_find(
563
0
          nexthop->interface,
564
0
          nexthop->mrib_nexthop_addr, true);
565
0
        if (!nbr
566
0
            && !if_is_loopback(nexthop->interface)) {
567
0
          if (PIM_DEBUG_PIM_NHT)
568
0
            zlog_debug(
569
0
              "%s: current nexthop does not have nbr ",
570
0
              __func__);
571
0
        } else {
572
          /* update metric even if the upstream
573
           * neighbor stays unchanged
574
           */
575
0
          nexthop->mrib_metric_preference =
576
0
            pnc->distance;
577
0
          nexthop->mrib_route_metric =
578
0
            pnc->metric;
579
0
          if (PIM_DEBUG_PIM_NHT)
580
0
            zlog_debug(
581
0
              "%s: (%pPA,%pPA)(%s) current nexthop %s is valid, skipping new path selection",
582
0
              __func__, &src,
583
0
              &grp_addr,
584
0
              pim->vrf->name,
585
0
              nexthop->interface->name);
586
0
          return 1;
587
0
        }
588
0
      }
589
0
    }
590
0
  }
591
592
  /*
593
   * Look up all interfaces and neighbors,
594
   * store for later usage
595
   */
596
0
  for (nh_node = pnc->nexthop, i = 0; nh_node;
597
0
       nh_node = nh_node->next, i++) {
598
0
    ifps[i] =
599
0
      if_lookup_by_index(nh_node->ifindex, pim->vrf->vrf_id);
600
0
    if (ifps[i]) {
601
0
#if PIM_IPV == 4
602
0
      pim_addr nhaddr = nh_node->gate.ipv4;
603
#else
604
      pim_addr nhaddr = nh_node->gate.ipv6;
605
#endif
606
0
      nbrs[i] = pim_neighbor_find(ifps[i], nhaddr, true);
607
0
      if (nbrs[i] || pim_if_connected_to_source(ifps[i], src))
608
0
        num_nbrs++;
609
0
    }
610
0
  }
611
0
  if (pim->ecmp_enable) {
612
0
    struct prefix src_pfx;
613
0
    uint32_t consider = pnc->nexthop_num;
614
615
0
    if (neighbor_needed && num_nbrs < consider)
616
0
      consider = num_nbrs;
617
618
0
    if (consider == 0)
619
0
      return 0;
620
621
    // PIM ECMP flag is enable then choose ECMP path.
622
0
    pim_addr_to_prefix(&src_pfx, src);
623
0
    hash_val = pim_compute_ecmp_hash(&src_pfx, grp);
624
0
    mod_val = hash_val % consider;
625
0
  }
626
627
0
  for (nh_node = pnc->nexthop; nh_node && (found == 0);
628
0
       nh_node = nh_node->next) {
629
0
    first_ifindex = nh_node->ifindex;
630
0
    ifp = ifps[nh_iter];
631
0
    if (!ifp) {
632
0
      if (PIM_DEBUG_PIM_NHT)
633
0
        zlog_debug(
634
0
          "%s %s: could not find interface for ifindex %d (address %pPA(%s))",
635
0
          __FILE__, __func__, first_ifindex, &src,
636
0
          pim->vrf->name);
637
0
      if (nh_iter == mod_val)
638
0
        mod_val++; // Select nexthpath
639
0
      nh_iter++;
640
0
      continue;
641
0
    }
642
643
0
    pim_ifp = ifp->info;
644
645
0
    if (!pim_ifp || !pim_ifp->pim_enable) {
646
0
      if (PIM_DEBUG_PIM_NHT)
647
0
        zlog_debug(
648
0
          "%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)",
649
0
          __func__, ifp->name, pim->vrf->name,
650
0
          first_ifindex, &src);
651
0
      if (nh_iter == mod_val)
652
0
        mod_val++; // Select nexthpath
653
0
      nh_iter++;
654
0
      continue;
655
0
    }
656
657
0
    if (neighbor_needed && !pim_if_connected_to_source(ifp, src)) {
658
0
      nbr = nbrs[nh_iter];
659
0
      if (!nbr && !if_is_loopback(ifp)) {
660
0
        if (PIM_DEBUG_PIM_NHT)
661
0
          zlog_debug(
662
0
            "%s: pim nbr not found on input interface %s(%s)",
663
0
            __func__, ifp->name,
664
0
            pim->vrf->name);
665
0
        if (nh_iter == mod_val)
666
0
          mod_val++; // Select nexthpath
667
0
        nh_iter++;
668
0
        continue;
669
0
      }
670
0
    }
671
672
0
    if (nh_iter == mod_val) {
673
0
      nexthop->interface = ifp;
674
0
#if PIM_IPV == 4
675
0
      nexthop->mrib_nexthop_addr = nh_node->gate.ipv4;
676
#else
677
      nexthop->mrib_nexthop_addr = nh_node->gate.ipv6;
678
#endif
679
0
      nexthop->mrib_metric_preference = pnc->distance;
680
0
      nexthop->mrib_route_metric = pnc->metric;
681
0
      nexthop->last_lookup = src;
682
0
      nexthop->last_lookup_time = pim_time_monotonic_usec();
683
0
      nexthop->nbr = nbr;
684
0
      found = 1;
685
0
      if (PIM_DEBUG_PIM_NHT)
686
0
        zlog_debug(
687
0
          "%s: (%pPA,%pPA)(%s) selected nhop interface %s addr %pPAs mod_val %u iter %d ecmp %d",
688
0
          __func__, &src, &grp_addr,
689
0
          pim->vrf->name, ifp->name, &nh_addr,
690
0
          mod_val, nh_iter, pim->ecmp_enable);
691
0
    }
692
0
    nh_iter++;
693
0
  }
694
695
0
  if (found)
696
0
    return 1;
697
0
  else
698
0
    return 0;
699
0
}
700
701
/* This API is used to parse Registered address nexthop update coming from Zebra
702
 */
703
int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
704
0
{
705
0
  struct nexthop *nexthop;
706
0
  struct nexthop *nhlist_head = NULL;
707
0
  struct nexthop *nhlist_tail = NULL;
708
0
  int i;
709
0
  struct pim_rpf rpf;
710
0
  struct pim_nexthop_cache *pnc = NULL;
711
0
  struct interface *ifp = NULL;
712
0
  struct vrf *vrf = vrf_lookup_by_id(vrf_id);
713
0
  struct pim_instance *pim;
714
0
  struct zapi_route nhr;
715
0
  struct prefix match;
716
717
0
  if (!vrf)
718
0
    return 0;
719
0
  pim = vrf->info;
720
721
0
  if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &nhr)) {
722
0
    zlog_err("%s: Decode of nexthop update from zebra failed",
723
0
       __func__);
724
0
    return 0;
725
0
  }
726
727
0
  rpf.rpf_addr = pim_addr_from_prefix(&match);
728
0
  pnc = pim_nexthop_cache_find(pim, &rpf);
729
0
  if (!pnc) {
730
0
    if (PIM_DEBUG_PIM_NHT)
731
0
      zlog_debug(
732
0
        "%s: Skipping NHT update, addr %pPA is not in local cached DB.",
733
0
        __func__, &rpf.rpf_addr);
734
0
    return 0;
735
0
  }
736
737
0
  pnc->last_update = pim_time_monotonic_usec();
738
739
0
  if (nhr.nexthop_num) {
740
0
    pnc->nexthop_num = 0;
741
742
0
    for (i = 0; i < nhr.nexthop_num; i++) {
743
0
      nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
744
0
      switch (nexthop->type) {
745
0
      case NEXTHOP_TYPE_IFINDEX:
746
        /*
747
         * Connected route (i.e. no nexthop), use
748
         * RPF address from nexthop cache (i.e.
749
         * destination) as PIM nexthop.
750
         */
751
0
#if PIM_IPV == 4
752
0
        nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
753
0
        nexthop->gate.ipv4 = pnc->rpf.rpf_addr;
754
#else
755
        nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
756
        nexthop->gate.ipv6 = pnc->rpf.rpf_addr;
757
#endif
758
0
        break;
759
0
#if PIM_IPV == 4
760
      /* RFC5549 IPv4-over-IPv6 nexthop handling:
761
       * if we get an IPv6 nexthop in IPv4 PIM, hunt down a
762
       * PIM neighbor and use that instead.
763
       */
764
0
      case NEXTHOP_TYPE_IPV6_IFINDEX: {
765
0
        struct interface *ifp1 = NULL;
766
0
        struct pim_neighbor *nbr = NULL;
767
768
0
        ifp1 = if_lookup_by_index(nexthop->ifindex,
769
0
                pim->vrf->vrf_id);
770
771
0
        if (!ifp1)
772
0
          nbr = NULL;
773
0
        else
774
          /* FIXME: should really use nbr's
775
           * secondary address list here
776
           */
777
0
          nbr = pim_neighbor_find_if(ifp1);
778
779
        /* Overwrite with Nbr address as NH addr */
780
0
        if (nbr)
781
0
          nexthop->gate.ipv4 = nbr->source_addr;
782
0
        else
783
          // Mark nexthop address to 0 until PIM
784
          // Nbr is resolved.
785
0
          nexthop->gate.ipv4 = PIMADDR_ANY;
786
787
0
        break;
788
0
      }
789
#else
790
      case NEXTHOP_TYPE_IPV6_IFINDEX:
791
#endif
792
0
      case NEXTHOP_TYPE_IPV6:
793
0
      case NEXTHOP_TYPE_IPV4:
794
0
      case NEXTHOP_TYPE_IPV4_IFINDEX:
795
0
      case NEXTHOP_TYPE_BLACKHOLE:
796
        /* nothing to do for the other nexthop types */
797
0
        break;
798
0
      }
799
800
0
      ifp = if_lookup_by_index(nexthop->ifindex,
801
0
             pim->vrf->vrf_id);
802
0
      if (!ifp) {
803
0
        if (PIM_DEBUG_PIM_NHT) {
804
0
          char buf[NEXTHOP_STRLEN];
805
0
          zlog_debug(
806
0
            "%s: could not find interface for ifindex %d(%s) (addr %s)",
807
0
            __func__, nexthop->ifindex,
808
0
            pim->vrf->name,
809
0
            nexthop2str(nexthop, buf,
810
0
                  sizeof(buf)));
811
0
        }
812
0
        nexthop_free(nexthop);
813
0
        continue;
814
0
      }
815
816
0
      if (PIM_DEBUG_PIM_NHT) {
817
0
#if PIM_IPV == 4
818
0
        pim_addr nhaddr = nexthop->gate.ipv4;
819
#else
820
        pim_addr nhaddr = nexthop->gate.ipv6;
821
#endif
822
0
        zlog_debug(
823
0
          "%s: NHT addr %pFX(%s) %d-nhop via %pPA(%s) type %d distance:%u metric:%u ",
824
0
          __func__, &match, pim->vrf->name, i + 1,
825
0
          &nhaddr, ifp->name, nexthop->type,
826
0
          nhr.distance, nhr.metric);
827
0
      }
828
829
0
      if (!ifp->info) {
830
        /*
831
         * Though Multicast is not enabled on this
832
         * Interface store it in database otheriwse we
833
         * may miss this update and this will not cause
834
         * any issue, because while choosing the path we
835
         * are ommitting the Interfaces which are not
836
         * multicast enabled
837
         */
838
0
        if (PIM_DEBUG_PIM_NHT) {
839
0
          char buf[NEXTHOP_STRLEN];
840
841
0
          zlog_debug(
842
0
            "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
843
0
            __func__, ifp->name,
844
0
            pim->vrf->name,
845
0
            nexthop->ifindex,
846
0
            nexthop2str(nexthop, buf,
847
0
                  sizeof(buf)));
848
0
        }
849
0
      }
850
851
0
      if (nhlist_tail) {
852
0
        nhlist_tail->next = nexthop;
853
0
        nhlist_tail = nexthop;
854
0
      } else {
855
0
        nhlist_tail = nexthop;
856
0
        nhlist_head = nexthop;
857
0
      }
858
859
      // Keep track of all nexthops, even PIM-disabled ones.
860
0
      pnc->nexthop_num++;
861
0
    }
862
    /* Reset existing pnc->nexthop before assigning new list */
863
0
    nexthops_free(pnc->nexthop);
864
0
    pnc->nexthop = nhlist_head;
865
0
    if (pnc->nexthop_num) {
866
0
      pnc->flags |= PIM_NEXTHOP_VALID;
867
0
      pnc->distance = nhr.distance;
868
0
      pnc->metric = nhr.metric;
869
0
    }
870
0
  } else {
871
0
    pnc->flags &= ~PIM_NEXTHOP_VALID;
872
0
    pnc->nexthop_num = nhr.nexthop_num;
873
0
    nexthops_free(pnc->nexthop);
874
0
    pnc->nexthop = NULL;
875
0
  }
876
0
  SET_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED);
877
878
0
  if (PIM_DEBUG_PIM_NHT)
879
0
    zlog_debug(
880
0
      "%s: NHT Update for %pFX(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
881
0
      __func__, &match, pim->vrf->name, nhr.nexthop_num,
882
0
      pnc->nexthop_num, vrf_id, pnc->upstream_hash->count,
883
0
      listcount(pnc->rp_list));
884
885
0
  pim_rpf_set_refresh_time(pim);
886
887
0
  if (listcount(pnc->rp_list))
888
0
    pim_update_rp_nh(pim, pnc);
889
0
  if (pnc->upstream_hash->count)
890
0
    pim_update_upstream_nh(pim, pnc);
891
892
0
  return 0;
893
0
}
894
895
int pim_ecmp_nexthop_lookup(struct pim_instance *pim,
896
          struct pim_nexthop *nexthop, pim_addr src,
897
          struct prefix *grp, int neighbor_needed)
898
156k
{
899
156k
  struct pim_nexthop_cache *pnc;
900
156k
  struct pim_zlookup_nexthop nexthop_tab[router->multipath];
901
156k
  struct pim_neighbor *nbrs[router->multipath], *nbr = NULL;
902
156k
  struct pim_rpf rpf;
903
156k
  int num_ifindex;
904
156k
  struct interface *ifps[router->multipath], *ifp;
905
156k
  int first_ifindex;
906
156k
  int found = 0;
907
156k
  uint8_t i = 0;
908
156k
  uint32_t hash_val = 0, mod_val = 0;
909
156k
  uint32_t num_nbrs = 0;
910
156k
  struct pim_interface *pim_ifp;
911
912
156k
  if (PIM_DEBUG_PIM_NHT_DETAIL)
913
0
    zlog_debug("%s: Looking up: %pPA(%s), last lookup time: %lld",
914
156k
         __func__, &src, pim->vrf->name,
915
156k
         nexthop->last_lookup_time);
916
917
156k
  rpf.rpf_addr = src;
918
919
156k
  pnc = pim_nexthop_cache_find(pim, &rpf);
920
156k
  if (pnc) {
921
156k
    if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_ANSWER_RECEIVED))
922
0
        return pim_ecmp_nexthop_search(pim, pnc, nexthop, src, grp,
923
0
               neighbor_needed);
924
156k
  }
925
926
156k
  memset(nexthop_tab, 0,
927
156k
         sizeof(struct pim_zlookup_nexthop) * router->multipath);
928
156k
  num_ifindex =
929
156k
    zclient_lookup_nexthop(pim, nexthop_tab, router->multipath, src,
930
156k
               PIM_NEXTHOP_LOOKUP_MAX);
931
156k
  if (num_ifindex < 1) {
932
156k
    if (PIM_DEBUG_PIM_NHT)
933
0
      zlog_warn(
934
156k
        "%s: could not find nexthop ifindex for address %pPA(%s)",
935
156k
        __func__, &src, pim->vrf->name);
936
156k
    return 0;
937
156k
  }
938
939
0
  memset(&nbrs, 0, sizeof(nbrs));
940
0
  memset(&ifps, 0, sizeof(ifps));
941
942
  /*
943
   * Look up all interfaces and neighbors,
944
   * store for later usage
945
   */
946
0
  for (i = 0; i < num_ifindex; i++) {
947
0
    ifps[i] = if_lookup_by_index(nexthop_tab[i].ifindex,
948
0
               pim->vrf->vrf_id);
949
0
    if (ifps[i]) {
950
0
      nbrs[i] = pim_neighbor_find(
951
0
        ifps[i], nexthop_tab[i].nexthop_addr, true);
952
953
0
      if (nbrs[i] || pim_if_connected_to_source(ifps[i], src))
954
0
        num_nbrs++;
955
0
    }
956
0
  }
957
958
  // If PIM ECMP enable then choose ECMP path.
959
0
  if (pim->ecmp_enable) {
960
0
    struct prefix src_pfx;
961
0
    uint32_t consider = num_ifindex;
962
963
0
    if (neighbor_needed && num_nbrs < consider)
964
0
      consider = num_nbrs;
965
966
0
    if (consider == 0)
967
0
      return 0;
968
969
0
    pim_addr_to_prefix(&src_pfx, src);
970
0
    hash_val = pim_compute_ecmp_hash(&src_pfx, grp);
971
0
    mod_val = hash_val % consider;
972
0
    if (PIM_DEBUG_PIM_NHT_DETAIL)
973
0
      zlog_debug("%s: hash_val %u mod_val %u", __func__,
974
0
           hash_val, mod_val);
975
0
  }
976
977
0
  i = 0;
978
0
  while (!found && (i < num_ifindex)) {
979
0
    first_ifindex = nexthop_tab[i].ifindex;
980
981
0
    ifp = ifps[i];
982
0
    if (!ifp) {
983
0
      if (PIM_DEBUG_PIM_NHT)
984
0
        zlog_debug(
985
0
          "%s %s: could not find interface for ifindex %d (address %pPA(%s))",
986
0
          __FILE__, __func__, first_ifindex, &src,
987
0
          pim->vrf->name);
988
0
      if (i == mod_val)
989
0
        mod_val++;
990
0
      i++;
991
0
      continue;
992
0
    }
993
994
0
    pim_ifp = ifp->info;
995
996
0
    if (!pim_ifp || !pim_ifp->pim_enable) {
997
0
      if (PIM_DEBUG_PIM_NHT)
998
0
        zlog_debug(
999
0
          "%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)",
1000
0
          __func__, ifp->name, pim->vrf->name,
1001
0
          first_ifindex, &src);
1002
0
      if (i == mod_val)
1003
0
        mod_val++;
1004
0
      i++;
1005
0
      continue;
1006
0
    }
1007
0
    if (neighbor_needed && !pim_if_connected_to_source(ifp, src)) {
1008
0
      nbr = nbrs[i];
1009
0
      if (PIM_DEBUG_PIM_NHT_DETAIL)
1010
0
        zlog_debug("ifp name: %s(%s), pim nbr: %p",
1011
0
             ifp->name, pim->vrf->name, nbr);
1012
0
      if (!nbr && !if_is_loopback(ifp)) {
1013
0
        if (i == mod_val)
1014
0
          mod_val++;
1015
0
        if (PIM_DEBUG_PIM_NHT)
1016
0
          zlog_debug(
1017
0
            "%s: NBR (%pPA) not found on input interface %s(%s) (RPF for source %pPA)",
1018
0
            __func__,
1019
0
            &nexthop_tab[i].nexthop_addr,
1020
0
            ifp->name, pim->vrf->name,
1021
0
            &src);
1022
0
        i++;
1023
0
        continue;
1024
0
      }
1025
0
    }
1026
1027
0
    if (i == mod_val) {
1028
0
      if (PIM_DEBUG_PIM_NHT)
1029
0
        zlog_debug(
1030
0
          "%s: found nhop %pPA for addr %pPA interface %s(%s) metric %d dist %d",
1031
0
          __func__, &nexthop_tab[i].nexthop_addr,
1032
0
          &src, ifp->name, pim->vrf->name,
1033
0
          nexthop_tab[i].route_metric,
1034
0
          nexthop_tab[i].protocol_distance);
1035
      /* update nexthop data */
1036
0
      nexthop->interface = ifp;
1037
0
      nexthop->mrib_nexthop_addr =
1038
0
        nexthop_tab[i].nexthop_addr;
1039
0
      nexthop->mrib_metric_preference =
1040
0
        nexthop_tab[i].protocol_distance;
1041
0
      nexthop->mrib_route_metric =
1042
0
        nexthop_tab[i].route_metric;
1043
0
      nexthop->last_lookup = src;
1044
0
      nexthop->last_lookup_time = pim_time_monotonic_usec();
1045
0
      nexthop->nbr = nbr;
1046
0
      found = 1;
1047
0
    }
1048
0
    i++;
1049
0
  }
1050
1051
0
  if (found)
1052
0
    return 1;
1053
0
  else
1054
0
    return 0;
1055
0
}
1056
1057
int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim, pim_addr src,
1058
             struct prefix *grp)
1059
0
{
1060
0
  struct pim_nexthop nhop;
1061
0
  int vif_index;
1062
0
  ifindex_t ifindex;
1063
1064
0
  memset(&nhop, 0, sizeof(nhop));
1065
0
  if (!pim_ecmp_nexthop_lookup(pim, &nhop, src, grp, 1)) {
1066
0
    if (PIM_DEBUG_PIM_NHT)
1067
0
      zlog_debug(
1068
0
        "%s: could not find nexthop ifindex for address %pPA(%s)",
1069
0
        __func__, &src, pim->vrf->name);
1070
0
    return -1;
1071
0
  }
1072
1073
0
  ifindex = nhop.interface->ifindex;
1074
0
  if (PIM_DEBUG_PIM_NHT)
1075
0
    zlog_debug(
1076
0
      "%s: found nexthop ifindex=%d (interface %s(%s)) for address %pPA",
1077
0
      __func__, ifindex,
1078
0
      ifindex2ifname(ifindex, pim->vrf->vrf_id),
1079
0
      pim->vrf->name, &src);
1080
1081
0
  vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
1082
1083
0
  if (vif_index < 0) {
1084
0
    if (PIM_DEBUG_PIM_NHT) {
1085
0
      zlog_debug(
1086
0
        "%s: low vif_index=%d(%s) < 1 nexthop for address %pPA",
1087
0
        __func__, vif_index, pim->vrf->name, &src);
1088
0
    }
1089
0
    return -2;
1090
0
  }
1091
1092
0
  return vif_index;
1093
0
}