1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2017 Cumulus Networks, Inc.
16 #include "lib/printfrr.h"
19 #include "pimd/pim_nht.h"
20 #include "pim_instance.h"
24 #include "pim_ifchannel.h"
25 #include "pim_mroute.h"
26 #include "pim_zebra.h"
27 #include "pim_upstream.h"
29 #include "pim_jp_agg.h"
30 #include "pim_zebra.h"
31 #include "pim_zlookup.h"
36 * pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
39 void pim_sendmsg_zebra_rnh(struct pim_instance
*pim
, struct zclient
*zclient
,
40 struct pim_nexthop_cache
*pnc
, int command
)
45 pim_addr_to_prefix(&p
, pnc
->rpf
.rpf_addr
);
46 ret
= zclient_send_rnh(zclient
, command
, &p
, SAFI_UNICAST
, false, false,
48 if (ret
== ZCLIENT_SEND_FAILURE
)
49 zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
51 if (PIM_DEBUG_PIM_NHT
)
53 "%s: NHT %sregistered addr %pFX(%s) with Zebra ret:%d ",
55 (command
== ZEBRA_NEXTHOP_REGISTER
) ? " " : "de", &p
,
61 struct pim_nexthop_cache
*pim_nexthop_cache_find(struct pim_instance
*pim
,
64 struct pim_nexthop_cache
*pnc
= NULL
;
65 struct pim_nexthop_cache lookup
;
67 lookup
.rpf
.rpf_addr
= rpf
->rpf_addr
;
68 pnc
= hash_lookup(pim
->rpf_hash
, &lookup
);
73 static struct pim_nexthop_cache
*pim_nexthop_cache_add(struct pim_instance
*pim
,
74 struct pim_rpf
*rpf_addr
)
76 struct pim_nexthop_cache
*pnc
;
79 pnc
= XCALLOC(MTYPE_PIM_NEXTHOP_CACHE
,
80 sizeof(struct pim_nexthop_cache
));
81 pnc
->rpf
.rpf_addr
= rpf_addr
->rpf_addr
;
83 pnc
= hash_get(pim
->rpf_hash
, pnc
, hash_alloc_intern
);
85 pnc
->rp_list
= list_new();
86 pnc
->rp_list
->cmp
= pim_rp_list_cmp
;
88 snprintfrr(hash_name
, sizeof(hash_name
), "PNC %pPA(%s) Upstream Hash",
89 &pnc
->rpf
.rpf_addr
, pim
->vrf
->name
);
90 pnc
->upstream_hash
= hash_create_size(8192, pim_upstream_hash_key
,
91 pim_upstream_equal
, hash_name
);
96 static struct pim_nexthop_cache
*pim_nht_get(struct pim_instance
*pim
,
99 struct pim_nexthop_cache
*pnc
= NULL
;
101 struct zclient
*zclient
= NULL
;
103 zclient
= pim_zebra_zclient_get();
104 memset(&rpf
, 0, sizeof(rpf
));
107 pnc
= pim_nexthop_cache_find(pim
, &rpf
);
109 pnc
= pim_nexthop_cache_add(pim
, &rpf
);
110 pim_sendmsg_zebra_rnh(pim
, zclient
, pnc
,
111 ZEBRA_NEXTHOP_REGISTER
);
112 if (PIM_DEBUG_PIM_NHT_DETAIL
)
114 "%s: NHT cache and zebra notification added for %pPA(%s)",
115 __func__
, &addr
, pim
->vrf
->name
);
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)
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
)
128 struct pim_nexthop_cache
*pnc
;
129 struct listnode
*ch_node
= NULL
;
131 pnc
= pim_nht_get(pim
, addr
);
133 assertf(up
|| rp
, "addr=%pPA", &addr
);
136 ch_node
= listnode_lookup(pnc
->rp_list
, rp
);
138 listnode_add_sort(pnc
->rp_list
, rp
);
142 (void)hash_get(pnc
->upstream_hash
, up
, hash_alloc_intern
);
144 if (CHECK_FLAG(pnc
->flags
, PIM_NEXTHOP_VALID
)) {
146 memcpy(out_pnc
, pnc
, sizeof(struct pim_nexthop_cache
));
153 void pim_nht_bsr_add(struct pim_instance
*pim
, pim_addr addr
)
155 struct pim_nexthop_cache
*pnc
;
157 pnc
= pim_nht_get(pim
, addr
);
162 static void pim_nht_drop_maybe(struct pim_instance
*pim
,
163 struct pim_nexthop_cache
*pnc
)
165 if (PIM_DEBUG_PIM_NHT
)
167 "%s: NHT %pPA(%s) rp_list count:%d upstream count:%ld BSR count:%u",
168 __func__
, &pnc
->rpf
.rpf_addr
, pim
->vrf
->name
,
169 pnc
->rp_list
->count
, pnc
->upstream_hash
->count
,
172 if (pnc
->rp_list
->count
== 0 && pnc
->upstream_hash
->count
== 0
173 && pnc
->bsr_count
== 0) {
174 struct zclient
*zclient
= pim_zebra_zclient_get();
176 pim_sendmsg_zebra_rnh(pim
, zclient
, pnc
,
177 ZEBRA_NEXTHOP_UNREGISTER
);
179 list_delete(&pnc
->rp_list
);
180 hash_free(pnc
->upstream_hash
);
182 hash_release(pim
->rpf_hash
, pnc
);
184 nexthops_free(pnc
->nexthop
);
185 XFREE(MTYPE_PIM_NEXTHOP_CACHE
, pnc
);
189 void pim_delete_tracked_nexthop(struct pim_instance
*pim
, pim_addr addr
,
190 struct pim_upstream
*up
, struct rp_info
*rp
)
192 struct pim_nexthop_cache
*pnc
= NULL
;
193 struct pim_nexthop_cache lookup
;
194 struct pim_upstream
*upstream
= NULL
;
196 /* Remove from RPF hash if it is the last entry */
197 lookup
.rpf
.rpf_addr
= addr
;
198 pnc
= hash_lookup(pim
->rpf_hash
, &lookup
);
200 zlog_warn("attempting to delete nonexistent NHT entry %pPA",
206 /* Release the (*, G)upstream from pnc->upstream_hash,
207 * whose Group belongs to the RP getting deleted
209 frr_each (rb_pim_upstream
, &pim
->upstream_head
, upstream
) {
211 struct rp_info
*trp_info
;
213 if (!pim_addr_is_any(upstream
->sg
.src
))
216 pim_addr_to_prefix(&grp
, upstream
->sg
.grp
);
217 trp_info
= pim_rp_find_match_group(pim
, &grp
);
219 hash_release(pnc
->upstream_hash
, upstream
);
221 listnode_delete(pnc
->rp_list
, rp
);
225 hash_release(pnc
->upstream_hash
, up
);
227 pim_nht_drop_maybe(pim
, pnc
);
230 void pim_nht_bsr_del(struct pim_instance
*pim
, pim_addr addr
)
232 struct pim_nexthop_cache
*pnc
= NULL
;
233 struct pim_nexthop_cache lookup
;
236 * Nothing to do here if the address to unregister
237 * is 0.0.0.0 as that the BSR has not been registered
240 if (pim_addr_is_any(addr
))
243 lookup
.rpf
.rpf_addr
= addr
;
245 pnc
= hash_lookup(pim
->rpf_hash
, &lookup
);
248 zlog_warn("attempting to delete nonexistent NHT BSR entry %pPA",
253 assertf(pnc
->bsr_count
> 0, "addr=%pPA", &addr
);
256 pim_nht_drop_maybe(pim
, pnc
);
259 bool pim_nht_bsr_rpf_check(struct pim_instance
*pim
, pim_addr bsr_addr
,
260 struct interface
*src_ifp
, pim_addr src_ip
)
262 struct pim_nexthop_cache
*pnc
= NULL
;
263 struct pim_nexthop_cache lookup
;
264 struct pim_neighbor
*nbr
= NULL
;
266 struct interface
*ifp
;
268 lookup
.rpf
.rpf_addr
= bsr_addr
;
270 pnc
= hash_lookup(pim
->rpf_hash
, &lookup
);
271 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
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 :(
282 struct pim_zlookup_nexthop nexthop_tab
[router
->multipath
];
284 struct interface
*ifp
= NULL
;
287 memset(nexthop_tab
, 0, sizeof(nexthop_tab
));
288 num_ifindex
= zclient_lookup_nexthop(
289 pim
, nexthop_tab
, router
->multipath
, bsr_addr
,
290 PIM_NEXTHOP_LOOKUP_MAX
);
292 if (num_ifindex
<= 0)
295 for (i
= 0; i
< num_ifindex
; i
++) {
296 struct pim_zlookup_nexthop
*znh
= &nexthop_tab
[i
];
298 /* pim_zlookup_nexthop has no ->type */
300 /* 1:1 match code below with znh instead of nh */
301 ifp
= if_lookup_by_index(znh
->ifindex
,
304 if (!ifp
|| !ifp
->info
)
307 if (if_is_loopback(ifp
) && if_is_loopback(src_ifp
))
310 nbr
= pim_neighbor_find(ifp
, znh
->nexthop_addr
, true);
314 return znh
->ifindex
== src_ifp
->ifindex
;
319 if (!CHECK_FLAG(pnc
->flags
, PIM_NEXTHOP_VALID
))
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
327 * so... only accept the first (IPv4) valid nexthop as source.
330 for (nh
= pnc
->nexthop
; nh
; nh
= nh
->next
) {
335 case NEXTHOP_TYPE_IPV4
:
336 if (nh
->ifindex
== IFINDEX_INTERNAL
)
340 case NEXTHOP_TYPE_IPV4_IFINDEX
:
341 nhaddr
= nh
->gate
.ipv4
;
343 case NEXTHOP_TYPE_IPV6
:
344 case NEXTHOP_TYPE_IPV6_IFINDEX
:
347 case NEXTHOP_TYPE_IPV6
:
348 if (nh
->ifindex
== IFINDEX_INTERNAL
)
352 case NEXTHOP_TYPE_IPV6_IFINDEX
:
353 nhaddr
= nh
->gate
.ipv6
;
355 case NEXTHOP_TYPE_IPV4
:
356 case NEXTHOP_TYPE_IPV4_IFINDEX
:
359 case NEXTHOP_TYPE_IFINDEX
:
363 case NEXTHOP_TYPE_BLACKHOLE
:
367 ifp
= if_lookup_by_index(nh
->ifindex
, pim
->vrf
->vrf_id
);
368 if (!ifp
|| !ifp
->info
)
371 if (if_is_loopback(ifp
) && if_is_loopback(src_ifp
))
374 /* MRIB (IGP) may be pointing at a router where PIM is down */
376 nbr
= pim_neighbor_find(ifp
, nhaddr
, true);
381 return nh
->ifindex
== src_ifp
->ifindex
;
386 void pim_rp_nexthop_del(struct rp_info
*rp_info
)
388 rp_info
->rp
.source_nexthop
.interface
= NULL
;
389 rp_info
->rp
.source_nexthop
.mrib_nexthop_addr
= PIMADDR_ANY
;
390 rp_info
->rp
.source_nexthop
.mrib_metric_preference
=
391 router
->infinite_assert_metric
.metric_preference
;
392 rp_info
->rp
.source_nexthop
.mrib_route_metric
=
393 router
->infinite_assert_metric
.route_metric
;
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
)
400 struct listnode
*node
= NULL
;
401 struct rp_info
*rp_info
= NULL
;
403 /*Traverse RP list and update each RP Nexthop info */
404 for (ALL_LIST_ELEMENTS_RO(pnc
->rp_list
, node
, rp_info
)) {
405 if (pim_rpf_addr_is_inaddr_any(&rp_info
->rp
))
408 // Compute PIM RPF using cached nexthop
409 if (!pim_ecmp_nexthop_lookup(pim
, &rp_info
->rp
.source_nexthop
,
410 rp_info
->rp
.rpf_addr
,
412 pim_rp_nexthop_del(rp_info
);
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
)
419 struct pim_instance
*pim
= (struct pim_instance
*)arg
;
420 struct pim_upstream
*up
= (struct pim_upstream
*)bucket
->data
;
422 enum pim_rpf_result rpf_result
;
425 old
.source_nexthop
.interface
= up
->rpf
.source_nexthop
.interface
;
426 rpf_result
= pim_rpf_update(pim
, up
, &old
, __func__
);
428 /* update kernel multicast forwarding cache (MFC); if the
429 * RPF nbr is now unreachable the MFC has already been updated
432 if (rpf_result
== PIM_RPF_CHANGED
)
433 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
435 if (rpf_result
== PIM_RPF_CHANGED
||
436 (rpf_result
== PIM_RPF_FAILURE
&& old
.source_nexthop
.interface
))
437 pim_zebra_upstream_rpf_changed(pim
, up
, &old
);
440 if (PIM_DEBUG_PIM_NHT
) {
442 "%s: NHT upstream %s(%s) old ifp %s new ifp %s",
443 __func__
, up
->sg_str
, pim
->vrf
->name
,
444 old
.source_nexthop
.interface
? old
.source_nexthop
447 up
->rpf
.source_nexthop
.interface
? up
->rpf
.source_nexthop
452 return HASHWALK_CONTINUE
;
455 static int pim_update_upstream_nh(struct pim_instance
*pim
,
456 struct pim_nexthop_cache
*pnc
)
458 hash_walk(pnc
->upstream_hash
, pim_update_upstream_nh_helper
, pim
);
460 pim_zebra_update_all_interfaces(pim
);
465 static int pim_upstream_nh_if_update_helper(struct hash_bucket
*bucket
,
468 struct pim_nexthop_cache
*pnc
= bucket
->data
;
469 struct pnc_hash_walk_data
*pwd
= arg
;
470 struct pim_instance
*pim
= pwd
->pim
;
471 struct interface
*ifp
= pwd
->ifp
;
472 struct nexthop
*nh_node
= NULL
;
473 ifindex_t first_ifindex
;
475 for (nh_node
= pnc
->nexthop
; nh_node
; nh_node
= nh_node
->next
) {
476 first_ifindex
= nh_node
->ifindex
;
477 if (ifp
!= if_lookup_by_index(first_ifindex
, pim
->vrf
->vrf_id
))
480 if (pnc
->upstream_hash
->count
) {
481 pim_update_upstream_nh(pim
, pnc
);
486 return HASHWALK_CONTINUE
;
489 void pim_upstream_nh_if_update(struct pim_instance
*pim
, struct interface
*ifp
)
491 struct pnc_hash_walk_data pwd
;
496 hash_walk(pim
->rpf_hash
, pim_upstream_nh_if_update_helper
, &pwd
);
499 uint32_t pim_compute_ecmp_hash(struct prefix
*src
, struct prefix
*grp
)
506 hash_val
= prefix_hash_key(src
);
508 hash_val
^= prefix_hash_key(grp
);
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
)
517 struct pim_neighbor
*nbrs
[router
->multipath
], *nbr
= NULL
;
518 struct interface
*ifps
[router
->multipath
];
519 struct nexthop
*nh_node
= NULL
;
520 ifindex_t first_ifindex
;
521 struct interface
*ifp
= NULL
;
522 uint32_t hash_val
= 0, mod_val
= 0;
523 uint8_t nh_iter
= 0, found
= 0;
524 uint32_t i
, num_nbrs
= 0;
525 struct pim_interface
*pim_ifp
;
527 if (!pnc
|| !pnc
->nexthop_num
|| !nexthop
)
530 pim_addr nh_addr
= nexthop
->mrib_nexthop_addr
;
531 pim_addr grp_addr
= pim_addr_from_prefix(grp
);
533 memset(&nbrs
, 0, sizeof(nbrs
));
534 memset(&ifps
, 0, sizeof(ifps
));
537 // Current Nexthop is VALID, check to stay on the current path.
538 if (nexthop
->interface
&& nexthop
->interface
->info
&&
539 (!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.
545 if (pim
->ecmp_rebalance_enable
== 0) {
546 uint8_t curr_route_valid
= 0;
547 // Check if current nexthop is present in new updated
549 // If the current nexthop is not valid, candidate to
550 // choose new Nexthop.
551 for (nh_node
= pnc
->nexthop
; nh_node
;
552 nh_node
= nh_node
->next
) {
553 curr_route_valid
= (nexthop
->interface
->ifindex
554 == nh_node
->ifindex
);
555 if (curr_route_valid
)
559 if (curr_route_valid
&&
560 !pim_if_connected_to_source(nexthop
->interface
,
562 nbr
= pim_neighbor_find(
564 nexthop
->mrib_nexthop_addr
, true);
566 && !if_is_loopback(nexthop
->interface
)) {
567 if (PIM_DEBUG_PIM_NHT
)
569 "%s: current nexthop does not have nbr ",
572 /* update metric even if the upstream
573 * neighbor stays unchanged
575 nexthop
->mrib_metric_preference
=
577 nexthop
->mrib_route_metric
=
579 if (PIM_DEBUG_PIM_NHT
)
581 "%s: (%pPA,%pPA)(%s) current nexthop %s is valid, skipping new path selection",
585 nexthop
->interface
->name
);
593 * Look up all interfaces and neighbors,
594 * store for later usage
596 for (nh_node
= pnc
->nexthop
, i
= 0; nh_node
;
597 nh_node
= nh_node
->next
, i
++) {
599 if_lookup_by_index(nh_node
->ifindex
, pim
->vrf
->vrf_id
);
602 pim_addr nhaddr
= nh_node
->gate
.ipv4
;
604 pim_addr nhaddr
= nh_node
->gate
.ipv6
;
606 nbrs
[i
] = pim_neighbor_find(ifps
[i
], nhaddr
, true);
607 if (nbrs
[i
] || pim_if_connected_to_source(ifps
[i
], src
))
611 if (pim
->ecmp_enable
) {
612 struct prefix src_pfx
;
613 uint32_t consider
= pnc
->nexthop_num
;
615 if (neighbor_needed
&& num_nbrs
< consider
)
621 // PIM ECMP flag is enable then choose ECMP path.
622 pim_addr_to_prefix(&src_pfx
, src
);
623 hash_val
= pim_compute_ecmp_hash(&src_pfx
, grp
);
624 mod_val
= hash_val
% consider
;
627 for (nh_node
= pnc
->nexthop
; nh_node
&& (found
== 0);
628 nh_node
= nh_node
->next
) {
629 first_ifindex
= nh_node
->ifindex
;
632 if (PIM_DEBUG_PIM_NHT
)
634 "%s %s: could not find interface for ifindex %d (address %pPA(%s))",
635 __FILE__
, __func__
, first_ifindex
, &src
,
637 if (nh_iter
== mod_val
)
638 mod_val
++; // Select nexthpath
645 if (!pim_ifp
|| !pim_ifp
->pim_enable
) {
646 if (PIM_DEBUG_PIM_NHT
)
648 "%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)",
649 __func__
, ifp
->name
, pim
->vrf
->name
,
650 first_ifindex
, &src
);
651 if (nh_iter
== mod_val
)
652 mod_val
++; // Select nexthpath
657 if (neighbor_needed
&& !pim_if_connected_to_source(ifp
, src
)) {
659 if (!nbr
&& !if_is_loopback(ifp
)) {
660 if (PIM_DEBUG_PIM_NHT
)
662 "%s: pim nbr not found on input interface %s(%s)",
665 if (nh_iter
== mod_val
)
666 mod_val
++; // Select nexthpath
672 if (nh_iter
== mod_val
) {
673 nexthop
->interface
= ifp
;
675 nexthop
->mrib_nexthop_addr
= nh_node
->gate
.ipv4
;
677 nexthop
->mrib_nexthop_addr
= nh_node
->gate
.ipv6
;
679 nexthop
->mrib_metric_preference
= pnc
->distance
;
680 nexthop
->mrib_route_metric
= pnc
->metric
;
681 nexthop
->last_lookup
= src
;
682 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
685 if (PIM_DEBUG_PIM_NHT
)
687 "%s: (%pPA,%pPA)(%s) selected nhop interface %s addr %pPAs mod_val %u iter %d ecmp %d",
688 __func__
, &src
, &grp_addr
,
689 pim
->vrf
->name
, ifp
->name
, &nh_addr
,
690 mod_val
, nh_iter
, pim
->ecmp_enable
);
701 /* This API is used to parse Registered address nexthop update coming from Zebra
703 int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS
)
705 struct nexthop
*nexthop
;
706 struct nexthop
*nhlist_head
= NULL
;
707 struct nexthop
*nhlist_tail
= NULL
;
710 struct pim_nexthop_cache
*pnc
= NULL
;
711 struct interface
*ifp
= NULL
;
712 struct vrf
*vrf
= vrf_lookup_by_id(vrf_id
);
713 struct pim_instance
*pim
;
714 struct zapi_route nhr
;
721 if (!zapi_nexthop_update_decode(zclient
->ibuf
, &match
, &nhr
)) {
722 zlog_err("%s: Decode of nexthop update from zebra failed",
727 rpf
.rpf_addr
= pim_addr_from_prefix(&match
);
728 pnc
= pim_nexthop_cache_find(pim
, &rpf
);
730 if (PIM_DEBUG_PIM_NHT
)
732 "%s: Skipping NHT update, addr %pPA is not in local cached DB.",
733 __func__
, &rpf
.rpf_addr
);
737 pnc
->last_update
= pim_time_monotonic_usec();
739 if (nhr
.nexthop_num
) {
740 pnc
->nexthop_num
= 0;
742 for (i
= 0; i
< nhr
.nexthop_num
; i
++) {
743 nexthop
= nexthop_from_zapi_nexthop(&nhr
.nexthops
[i
]);
744 switch (nexthop
->type
) {
745 case NEXTHOP_TYPE_IFINDEX
:
747 * Connected route (i.e. no nexthop), use
748 * RPF address from nexthop cache (i.e.
749 * destination) as PIM nexthop.
752 nexthop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
753 nexthop
->gate
.ipv4
= pnc
->rpf
.rpf_addr
;
755 nexthop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
756 nexthop
->gate
.ipv6
= pnc
->rpf
.rpf_addr
;
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.
764 case NEXTHOP_TYPE_IPV6_IFINDEX
: {
765 struct interface
*ifp1
= NULL
;
766 struct pim_neighbor
*nbr
= NULL
;
768 ifp1
= if_lookup_by_index(nexthop
->ifindex
,
774 /* FIXME: should really use nbr's
775 * secondary address list here
777 nbr
= pim_neighbor_find_if(ifp1
);
779 /* Overwrite with Nbr address as NH addr */
781 nexthop
->gate
.ipv4
= nbr
->source_addr
;
783 // Mark nexthop address to 0 until PIM
785 nexthop
->gate
.ipv4
= PIMADDR_ANY
;
790 case NEXTHOP_TYPE_IPV6_IFINDEX
:
792 case NEXTHOP_TYPE_IPV6
:
793 case NEXTHOP_TYPE_IPV4
:
794 case NEXTHOP_TYPE_IPV4_IFINDEX
:
795 case NEXTHOP_TYPE_BLACKHOLE
:
796 /* nothing to do for the other nexthop types */
800 ifp
= if_lookup_by_index(nexthop
->ifindex
,
803 if (PIM_DEBUG_PIM_NHT
) {
804 char buf
[NEXTHOP_STRLEN
];
806 "%s: could not find interface for ifindex %d(%s) (addr %s)",
807 __func__
, nexthop
->ifindex
,
809 nexthop2str(nexthop
, buf
,
812 nexthop_free(nexthop
);
816 if (PIM_DEBUG_PIM_NHT
) {
818 pim_addr nhaddr
= nexthop
->gate
.ipv4
;
820 pim_addr nhaddr
= nexthop
->gate
.ipv6
;
823 "%s: NHT addr %pFX(%s) %d-nhop via %pPA(%s) type %d distance:%u metric:%u ",
824 __func__
, &match
, pim
->vrf
->name
, i
+ 1,
825 &nhaddr
, ifp
->name
, nexthop
->type
,
826 nhr
.distance
, nhr
.metric
);
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
838 if (PIM_DEBUG_PIM_NHT
) {
839 char buf
[NEXTHOP_STRLEN
];
842 "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
846 nexthop2str(nexthop
, buf
,
852 nhlist_tail
->next
= nexthop
;
853 nhlist_tail
= nexthop
;
855 nhlist_tail
= nexthop
;
856 nhlist_head
= nexthop
;
859 // Keep track of all nexthops, even PIM-disabled ones.
862 /* Reset existing pnc->nexthop before assigning new list */
863 nexthops_free(pnc
->nexthop
);
864 pnc
->nexthop
= nhlist_head
;
865 if (pnc
->nexthop_num
) {
866 pnc
->flags
|= PIM_NEXTHOP_VALID
;
867 pnc
->distance
= nhr
.distance
;
868 pnc
->metric
= nhr
.metric
;
871 pnc
->flags
&= ~PIM_NEXTHOP_VALID
;
872 pnc
->nexthop_num
= nhr
.nexthop_num
;
873 nexthops_free(pnc
->nexthop
);
876 SET_FLAG(pnc
->flags
, PIM_NEXTHOP_ANSWER_RECEIVED
);
878 if (PIM_DEBUG_PIM_NHT
)
880 "%s: NHT Update for %pFX(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
881 __func__
, &match
, pim
->vrf
->name
, nhr
.nexthop_num
,
882 pnc
->nexthop_num
, vrf_id
, pnc
->upstream_hash
->count
,
883 listcount(pnc
->rp_list
));
885 pim_rpf_set_refresh_time(pim
);
887 if (listcount(pnc
->rp_list
))
888 pim_update_rp_nh(pim
, pnc
);
889 if (pnc
->upstream_hash
->count
)
890 pim_update_upstream_nh(pim
, pnc
);
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
)
899 struct pim_nexthop_cache
*pnc
;
900 struct pim_zlookup_nexthop nexthop_tab
[router
->multipath
];
901 struct pim_neighbor
*nbrs
[router
->multipath
], *nbr
= NULL
;
904 struct interface
*ifps
[router
->multipath
], *ifp
;
908 uint32_t hash_val
= 0, mod_val
= 0;
909 uint32_t num_nbrs
= 0;
910 struct pim_interface
*pim_ifp
;
912 if (PIM_DEBUG_PIM_NHT_DETAIL
)
913 zlog_debug("%s: Looking up: %pPA(%s), last lookup time: %lld",
914 __func__
, &src
, pim
->vrf
->name
,
915 nexthop
->last_lookup_time
);
919 pnc
= pim_nexthop_cache_find(pim
, &rpf
);
921 if (CHECK_FLAG(pnc
->flags
, PIM_NEXTHOP_ANSWER_RECEIVED
))
922 return pim_ecmp_nexthop_search(pim
, pnc
, nexthop
, src
, grp
,
926 memset(nexthop_tab
, 0,
927 sizeof(struct pim_zlookup_nexthop
) * router
->multipath
);
929 zclient_lookup_nexthop(pim
, nexthop_tab
, router
->multipath
, src
,
930 PIM_NEXTHOP_LOOKUP_MAX
);
931 if (num_ifindex
< 1) {
932 if (PIM_DEBUG_PIM_NHT
)
934 "%s: could not find nexthop ifindex for address %pPA(%s)",
935 __func__
, &src
, pim
->vrf
->name
);
939 memset(&nbrs
, 0, sizeof(nbrs
));
940 memset(&ifps
, 0, sizeof(ifps
));
943 * Look up all interfaces and neighbors,
944 * store for later usage
946 for (i
= 0; i
< num_ifindex
; i
++) {
947 ifps
[i
] = if_lookup_by_index(nexthop_tab
[i
].ifindex
,
950 nbrs
[i
] = pim_neighbor_find(
951 ifps
[i
], nexthop_tab
[i
].nexthop_addr
, true);
953 if (nbrs
[i
] || pim_if_connected_to_source(ifps
[i
], src
))
958 // If PIM ECMP enable then choose ECMP path.
959 if (pim
->ecmp_enable
) {
960 struct prefix src_pfx
;
961 uint32_t consider
= num_ifindex
;
963 if (neighbor_needed
&& num_nbrs
< consider
)
969 pim_addr_to_prefix(&src_pfx
, src
);
970 hash_val
= pim_compute_ecmp_hash(&src_pfx
, grp
);
971 mod_val
= hash_val
% consider
;
972 if (PIM_DEBUG_PIM_NHT_DETAIL
)
973 zlog_debug("%s: hash_val %u mod_val %u", __func__
,
978 while (!found
&& (i
< num_ifindex
)) {
979 first_ifindex
= nexthop_tab
[i
].ifindex
;
983 if (PIM_DEBUG_PIM_NHT
)
985 "%s %s: could not find interface for ifindex %d (address %pPA(%s))",
986 __FILE__
, __func__
, first_ifindex
, &src
,
996 if (!pim_ifp
|| !pim_ifp
->pim_enable
) {
997 if (PIM_DEBUG_PIM_NHT
)
999 "%s: pim not enabled on input interface %s(%s) (ifindex=%d, RPF for source %pPA)",
1000 __func__
, ifp
->name
, pim
->vrf
->name
,
1001 first_ifindex
, &src
);
1007 if (neighbor_needed
&& !pim_if_connected_to_source(ifp
, src
)) {
1009 if (PIM_DEBUG_PIM_NHT_DETAIL
)
1010 zlog_debug("ifp name: %s(%s), pim nbr: %p",
1011 ifp
->name
, pim
->vrf
->name
, nbr
);
1012 if (!nbr
&& !if_is_loopback(ifp
)) {
1015 if (PIM_DEBUG_PIM_NHT
)
1017 "%s: NBR (%pPA) not found on input interface %s(%s) (RPF for source %pPA)",
1019 &nexthop_tab
[i
].nexthop_addr
,
1020 ifp
->name
, pim
->vrf
->name
,
1028 if (PIM_DEBUG_PIM_NHT
)
1030 "%s: found nhop %pPA for addr %pPA interface %s(%s) metric %d dist %d",
1031 __func__
, &nexthop_tab
[i
].nexthop_addr
,
1032 &src
, ifp
->name
, pim
->vrf
->name
,
1033 nexthop_tab
[i
].route_metric
,
1034 nexthop_tab
[i
].protocol_distance
);
1035 /* update nexthop data */
1036 nexthop
->interface
= ifp
;
1037 nexthop
->mrib_nexthop_addr
=
1038 nexthop_tab
[i
].nexthop_addr
;
1039 nexthop
->mrib_metric_preference
=
1040 nexthop_tab
[i
].protocol_distance
;
1041 nexthop
->mrib_route_metric
=
1042 nexthop_tab
[i
].route_metric
;
1043 nexthop
->last_lookup
= src
;
1044 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
1057 int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance
*pim
, pim_addr src
,
1060 struct pim_nexthop nhop
;
1064 memset(&nhop
, 0, sizeof(nhop
));
1065 if (!pim_ecmp_nexthop_lookup(pim
, &nhop
, src
, grp
, 1)) {
1066 if (PIM_DEBUG_PIM_NHT
)
1068 "%s: could not find nexthop ifindex for address %pPA(%s)",
1069 __func__
, &src
, pim
->vrf
->name
);
1073 ifindex
= nhop
.interface
->ifindex
;
1074 if (PIM_DEBUG_PIM_NHT
)
1076 "%s: found nexthop ifindex=%d (interface %s(%s)) for address %pPA",
1078 ifindex2ifname(ifindex
, pim
->vrf
->vrf_id
),
1079 pim
->vrf
->name
, &src
);
1081 vif_index
= pim_if_find_vifindex_by_ifindex(pim
, ifindex
);
1083 if (vif_index
< 0) {
1084 if (PIM_DEBUG_PIM_NHT
) {
1086 "%s: low vif_index=%d(%s) < 1 nexthop for address %pPA",
1087 __func__
, vif_index
, pim
->vrf
->name
, &src
);