1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
15 #include "lib_errors.h"
18 #include "pim_instance.h"
19 #include "pim_neighbor.h"
22 #include "pim_iface.h"
24 #include "pim_upstream.h"
25 #include "pim_ifchannel.h"
27 #include "pim_zebra.h"
29 #include "pim_jp_agg.h"
31 #include "pim_register.h"
33 static void dr_election_by_addr(struct interface
*ifp
)
35 struct pim_interface
*pim_ifp
;
36 struct listnode
*node
;
37 struct pim_neighbor
*neigh
;
42 pim_ifp
->pim_dr_addr
= pim_ifp
->primary_address
;
44 if (PIM_DEBUG_PIM_TRACE
) {
45 zlog_debug("%s: on interface %s", __func__
, ifp
->name
);
48 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
, node
, neigh
)) {
49 if (pim_addr_cmp(neigh
->source_addr
, pim_ifp
->pim_dr_addr
) > 0)
50 pim_ifp
->pim_dr_addr
= neigh
->source_addr
;
54 static void dr_election_by_pri(struct interface
*ifp
)
56 struct pim_interface
*pim_ifp
;
57 struct listnode
*node
;
58 struct pim_neighbor
*neigh
;
64 pim_ifp
->pim_dr_addr
= pim_ifp
->primary_address
;
65 dr_pri
= pim_ifp
->pim_dr_priority
;
67 if (PIM_DEBUG_PIM_TRACE
) {
68 zlog_debug("%s: dr pri %u on interface %s", __func__
, dr_pri
,
72 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
, node
, neigh
)) {
73 if (PIM_DEBUG_PIM_TRACE
) {
74 zlog_info("%s: neigh pri %u addr %pPA if dr addr %pPA",
75 __func__
, neigh
->dr_priority
,
76 &neigh
->source_addr
, &pim_ifp
->pim_dr_addr
);
78 if ((neigh
->dr_priority
> dr_pri
) ||
79 ((neigh
->dr_priority
== dr_pri
) &&
80 (pim_addr_cmp(neigh
->source_addr
, pim_ifp
->pim_dr_addr
) >
82 pim_ifp
->pim_dr_addr
= neigh
->source_addr
;
83 dr_pri
= neigh
->dr_priority
;
89 RFC 4601: 4.3.2. DR Election
91 A router's idea of the current DR on an interface can change when a
92 PIM Hello message is received, when a neighbor times out, or when a
93 router's own DR Priority changes.
95 int pim_if_dr_election(struct interface
*ifp
)
97 struct pim_interface
*pim_ifp
= ifp
->info
;
100 ++pim_ifp
->pim_dr_election_count
;
102 old_dr_addr
= pim_ifp
->pim_dr_addr
;
104 if (pim_ifp
->pim_dr_num_nondrpri_neighbors
) {
105 dr_election_by_addr(ifp
);
107 dr_election_by_pri(ifp
);
111 if (pim_addr_cmp(old_dr_addr
, pim_ifp
->pim_dr_addr
)) {
113 if (PIM_DEBUG_PIM_EVENTS
)
115 "%s: DR was %pPA now is %pPA on interface %s",
116 __func__
, &old_dr_addr
, &pim_ifp
->pim_dr_addr
,
119 pim_ifp
->pim_dr_election_last
=
120 pim_time_monotonic_sec(); /* timestamp */
121 ++pim_ifp
->pim_dr_election_changes
;
122 pim_if_update_join_desired(pim_ifp
);
123 pim_if_update_could_assert(ifp
);
124 pim_if_update_assert_tracking_desired(ifp
);
126 if (PIM_I_am_DR(pim_ifp
))
127 pim_ifp
->am_i_dr
= true;
129 if (pim_ifp
->am_i_dr
== true) {
130 pim_reg_del_on_couldreg_fail(ifp
);
131 pim_ifp
->am_i_dr
= false;
141 static void update_dr_priority(struct pim_neighbor
*neigh
,
142 pim_hello_options hello_options
,
143 uint32_t dr_priority
)
145 pim_hello_options will_set_pri
; /* boolean */
146 pim_hello_options bit_flip
; /* boolean */
147 pim_hello_options pri_change
; /* boolean */
150 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_DR_PRIORITY
);
152 bit_flip
= (will_set_pri
153 != PIM_OPTION_IS_SET(neigh
->hello_options
,
154 PIM_OPTION_MASK_DR_PRIORITY
));
157 struct pim_interface
*pim_ifp
= neigh
->interface
->info
;
159 /* update num. of neighbors without dr_pri */
162 --pim_ifp
->pim_dr_num_nondrpri_neighbors
;
164 ++pim_ifp
->pim_dr_num_nondrpri_neighbors
;
168 pri_change
= (bit_flip
|| (neigh
->dr_priority
!= dr_priority
));
171 neigh
->dr_priority
= dr_priority
;
173 neigh
->dr_priority
= 0; /* cosmetic unset */
178 RFC 4601: 4.3.2. DR Election
180 A router's idea of the current DR on an interface can change
182 PIM Hello message is received, when a neighbor times out, or
184 router's own DR Priority changes.
187 neigh
->interface
); // router's own DR Priority changes
191 static void on_neighbor_timer(struct event
*t
)
193 struct pim_neighbor
*neigh
;
194 struct interface
*ifp
;
197 neigh
= THREAD_ARG(t
);
199 ifp
= neigh
->interface
;
201 if (PIM_DEBUG_PIM_TRACE
)
203 "Expired %d sec holdtime for neighbor %pPA on interface %s",
204 neigh
->holdtime
, &neigh
->source_addr
, ifp
->name
);
206 snprintf(msg
, sizeof(msg
), "%d-sec holdtime expired", neigh
->holdtime
);
207 pim_neighbor_delete(ifp
, neigh
, msg
);
210 RFC 4601: 4.3.2. DR Election
212 A router's idea of the current DR on an interface can change when a
213 PIM Hello message is received, when a neighbor times out, or when a
214 router's own DR Priority changes.
216 pim_if_dr_election(ifp
); // neighbor times out
219 void pim_neighbor_timer_reset(struct pim_neighbor
*neigh
, uint16_t holdtime
)
221 neigh
->holdtime
= holdtime
;
223 THREAD_OFF(neigh
->t_expire_timer
);
226 0xFFFF is request for no holdtime
228 if (neigh
->holdtime
== 0xFFFF) {
232 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
233 zlog_debug("%s: starting %u sec timer for neighbor %pPA on %s",
234 __func__
, neigh
->holdtime
, &neigh
->source_addr
,
235 neigh
->interface
->name
);
237 thread_add_timer(router
->master
, on_neighbor_timer
, neigh
,
238 neigh
->holdtime
, &neigh
->t_expire_timer
);
241 static void on_neighbor_jp_timer(struct event
*t
)
243 struct pim_neighbor
*neigh
= THREAD_ARG(t
);
246 if (PIM_DEBUG_PIM_TRACE
)
247 zlog_debug("%s:Sending JP Agg to %pPA on %s with %d groups",
248 __func__
, &neigh
->source_addr
,
249 neigh
->interface
->name
,
250 neigh
->upstream_jp_agg
->count
);
252 rpf
.source_nexthop
.interface
= neigh
->interface
;
253 rpf
.rpf_addr
= neigh
->source_addr
;
254 pim_joinprune_send(&rpf
, neigh
->upstream_jp_agg
);
256 thread_add_timer(router
->master
, on_neighbor_jp_timer
, neigh
,
257 router
->t_periodic
, &neigh
->jp_timer
);
260 static void pim_neighbor_start_jp_timer(struct pim_neighbor
*neigh
)
262 THREAD_OFF(neigh
->jp_timer
);
263 thread_add_timer(router
->master
, on_neighbor_jp_timer
, neigh
,
264 router
->t_periodic
, &neigh
->jp_timer
);
267 static struct pim_neighbor
*
268 pim_neighbor_new(struct interface
*ifp
, pim_addr source_addr
,
269 pim_hello_options hello_options
, uint16_t holdtime
,
270 uint16_t propagation_delay
, uint16_t override_interval
,
271 uint32_t dr_priority
, uint32_t generation_id
,
272 struct list
*addr_list
)
274 struct pim_interface
*pim_ifp
;
275 struct pim_neighbor
*neigh
;
281 neigh
= XCALLOC(MTYPE_PIM_NEIGHBOR
, sizeof(*neigh
));
283 neigh
->creation
= pim_time_monotonic_sec();
284 neigh
->source_addr
= source_addr
;
285 neigh
->hello_options
= hello_options
;
286 neigh
->propagation_delay_msec
= propagation_delay
;
287 neigh
->override_interval_msec
= override_interval
;
288 neigh
->dr_priority
= dr_priority
;
289 neigh
->generation_id
= generation_id
;
290 neigh
->prefix_list
= addr_list
;
291 neigh
->t_expire_timer
= NULL
;
292 neigh
->interface
= ifp
;
294 neigh
->upstream_jp_agg
= list_new();
295 neigh
->upstream_jp_agg
->cmp
= pim_jp_agg_group_list_cmp
;
296 neigh
->upstream_jp_agg
->del
=
297 (void (*)(void *))pim_jp_agg_group_list_free
;
298 pim_neighbor_start_jp_timer(neigh
);
300 pim_neighbor_timer_reset(neigh
, holdtime
);
302 * The pim_ifstat_hello_sent variable is used to decide if
303 * we should expedite a hello out the interface. If we
304 * establish a new neighbor, we unfortunately need to
305 * reset the value so that we can know to hurry up and
308 PIM_IF_FLAG_UNSET_HELLO_SENT(pim_ifp
->flags
);
310 if (PIM_DEBUG_PIM_EVENTS
)
311 zlog_debug("%s: creating PIM neighbor %pPA on interface %s",
312 __func__
, &source_addr
, ifp
->name
);
314 zlog_notice("PIM NEIGHBOR UP: neighbor %pPA on interface %s",
315 &source_addr
, ifp
->name
);
317 if (neigh
->propagation_delay_msec
318 > pim_ifp
->pim_neighbors_highest_propagation_delay_msec
) {
319 pim_ifp
->pim_neighbors_highest_propagation_delay_msec
=
320 neigh
->propagation_delay_msec
;
322 if (neigh
->override_interval_msec
323 > pim_ifp
->pim_neighbors_highest_override_interval_msec
) {
324 pim_ifp
->pim_neighbors_highest_override_interval_msec
=
325 neigh
->override_interval_msec
;
328 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
329 PIM_OPTION_MASK_LAN_PRUNE_DELAY
)) {
330 /* update num. of neighbors without hello option lan_delay */
331 ++pim_ifp
->pim_number_of_nonlandelay_neighbors
;
334 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
335 PIM_OPTION_MASK_DR_PRIORITY
)) {
336 /* update num. of neighbors without hello option dr_pri */
337 ++pim_ifp
->pim_dr_num_nondrpri_neighbors
;
340 // Register PIM Neighbor with BFD
341 pim_bfd_info_nbr_create(pim_ifp
, neigh
);
346 static void delete_prefix_list(struct pim_neighbor
*neigh
)
348 if (neigh
->prefix_list
) {
350 #ifdef DUMP_PREFIX_LIST
351 struct listnode
*p_node
;
353 int list_size
= neigh
->prefix_list
354 ? (int)listcount(neigh
->prefix_list
)
357 for (ALL_LIST_ELEMENTS_RO(neigh
->prefix_list
, p_node
, p
)) {
359 "%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%pFXh [%d/%d]",
360 __func__
, (unsigned)neigh
,
361 (unsigned)neigh
->prefix_list
, (unsigned)p
, p
, i
,
367 list_delete(&neigh
->prefix_list
);
371 void pim_neighbor_free(struct pim_neighbor
*neigh
)
373 assert(!neigh
->t_expire_timer
);
375 delete_prefix_list(neigh
);
377 list_delete(&neigh
->upstream_jp_agg
);
378 THREAD_OFF(neigh
->jp_timer
);
380 bfd_sess_free(&neigh
->bfd_session
);
382 XFREE(MTYPE_PIM_NEIGHBOR
, neigh
);
385 struct pim_neighbor
*pim_neighbor_find_by_secondary(struct interface
*ifp
,
388 struct pim_interface
*pim_ifp
;
389 struct listnode
*node
, *pnode
;
390 struct pim_neighbor
*neigh
;
393 if (!ifp
|| !ifp
->info
)
398 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
, node
, neigh
)) {
399 for (ALL_LIST_ELEMENTS_RO(neigh
->prefix_list
, pnode
, p
)) {
400 if (prefix_same(p
, src
))
408 struct pim_neighbor
*pim_neighbor_find(struct interface
*ifp
,
409 pim_addr source_addr
, bool secondary
)
411 struct pim_interface
*pim_ifp
;
412 struct listnode
*node
;
413 struct pim_neighbor
*neigh
;
422 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
, node
, neigh
)) {
423 if (!pim_addr_cmp(source_addr
, neigh
->source_addr
)) {
431 pim_addr_to_prefix(&p
, source_addr
);
432 return pim_neighbor_find_by_secondary(ifp
, &p
);
439 * Find the *one* interface out
440 * this interface. If more than
443 struct pim_neighbor
*pim_neighbor_find_if(struct interface
*ifp
)
445 struct pim_interface
*pim_ifp
= ifp
->info
;
447 if (!pim_ifp
|| pim_ifp
->pim_neighbor_list
->count
!= 1)
450 return listnode_head(pim_ifp
->pim_neighbor_list
);
453 struct pim_neighbor
*
454 pim_neighbor_add(struct interface
*ifp
, pim_addr source_addr
,
455 pim_hello_options hello_options
, uint16_t holdtime
,
456 uint16_t propagation_delay
, uint16_t override_interval
,
457 uint32_t dr_priority
, uint32_t generation_id
,
458 struct list
*addr_list
, int send_hello_now
)
460 struct pim_interface
*pim_ifp
;
461 struct pim_neighbor
*neigh
;
463 neigh
= pim_neighbor_new(ifp
, source_addr
, hello_options
, holdtime
,
464 propagation_delay
, override_interval
,
465 dr_priority
, generation_id
, addr_list
);
473 listnode_add(pim_ifp
->pim_neighbor_list
, neigh
);
475 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
476 zlog_debug("%s: neighbor %pPA added ", __func__
, &source_addr
);
478 RFC 4601: 4.3.2. DR Election
480 A router's idea of the current DR on an interface can change when a
481 PIM Hello message is received, when a neighbor times out, or when a
482 router's own DR Priority changes.
484 pim_if_dr_election(neigh
->interface
); // new neighbor -- should not
485 // trigger dr election...
488 RFC 4601: 4.3.1. Sending Hello Messages
490 To allow new or rebooting routers to learn of PIM neighbors quickly,
491 when a Hello message is received from a new neighbor, or a Hello
492 message with a new GenID is received from an existing neighbor, a
493 new Hello message should be sent on this interface after a
494 randomized delay between 0 and Triggered_Hello_Delay.
496 This is a bit silly to do it that way. If I get a new
497 genid we need to send the hello *now* because we've
498 lined up a bunch of join/prune messages to go out the
502 pim_hello_restart_now(ifp
);
504 pim_hello_restart_triggered(neigh
->interface
);
506 pim_upstream_find_new_rpf(pim_ifp
->pim
);
508 /* RNH can send nexthop update prior to PIM neibhor UP
509 in that case nexthop cache would not consider this neighbor
511 Upon PIM neighbor UP, iterate all RPs and update
512 nexthop cache with this neighbor.
514 pim_resolve_rp_nh(pim_ifp
->pim
, neigh
);
516 pim_rp_setup(pim_ifp
->pim
);
518 sched_rpf_cache_refresh(pim_ifp
->pim
);
522 static uint16_t find_neighbors_next_highest_propagation_delay_msec(
523 struct interface
*ifp
, struct pim_neighbor
*highest_neigh
)
525 struct pim_interface
*pim_ifp
;
526 struct listnode
*neigh_node
;
527 struct pim_neighbor
*neigh
;
528 uint16_t next_highest_delay_msec
;
533 next_highest_delay_msec
= pim_ifp
->pim_propagation_delay_msec
;
535 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
, neigh_node
,
537 if (neigh
== highest_neigh
)
539 if (neigh
->propagation_delay_msec
> next_highest_delay_msec
)
540 next_highest_delay_msec
= neigh
->propagation_delay_msec
;
543 return next_highest_delay_msec
;
546 static uint16_t find_neighbors_next_highest_override_interval_msec(
547 struct interface
*ifp
, struct pim_neighbor
*highest_neigh
)
549 struct pim_interface
*pim_ifp
;
550 struct listnode
*neigh_node
;
551 struct pim_neighbor
*neigh
;
552 uint16_t next_highest_interval_msec
;
557 next_highest_interval_msec
= pim_ifp
->pim_override_interval_msec
;
559 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
, neigh_node
,
561 if (neigh
== highest_neigh
)
563 if (neigh
->override_interval_msec
> next_highest_interval_msec
)
564 next_highest_interval_msec
=
565 neigh
->override_interval_msec
;
568 return next_highest_interval_msec
;
571 void pim_neighbor_delete(struct interface
*ifp
, struct pim_neighbor
*neigh
,
572 const char *delete_message
)
574 struct pim_interface
*pim_ifp
;
579 zlog_notice("PIM NEIGHBOR DOWN: neighbor %pPA on interface %s: %s",
580 &neigh
->source_addr
, ifp
->name
, delete_message
);
582 THREAD_OFF(neigh
->t_expire_timer
);
584 pim_if_assert_on_neighbor_down(ifp
, neigh
->source_addr
);
586 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
587 PIM_OPTION_MASK_LAN_PRUNE_DELAY
)) {
588 /* update num. of neighbors without hello option lan_delay */
590 --pim_ifp
->pim_number_of_nonlandelay_neighbors
;
593 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
594 PIM_OPTION_MASK_DR_PRIORITY
)) {
595 /* update num. of neighbors without dr_pri */
597 --pim_ifp
->pim_dr_num_nondrpri_neighbors
;
600 assert(neigh
->propagation_delay_msec
601 <= pim_ifp
->pim_neighbors_highest_propagation_delay_msec
);
602 assert(neigh
->override_interval_msec
603 <= pim_ifp
->pim_neighbors_highest_override_interval_msec
);
605 if (pim_if_lan_delay_enabled(ifp
)) {
607 /* will delete a neighbor with highest propagation delay? */
608 if (neigh
->propagation_delay_msec
609 == pim_ifp
->pim_neighbors_highest_propagation_delay_msec
) {
610 /* then find the next highest propagation delay */
611 pim_ifp
->pim_neighbors_highest_propagation_delay_msec
=
612 find_neighbors_next_highest_propagation_delay_msec(
616 /* will delete a neighbor with highest override interval? */
617 if (neigh
->override_interval_msec
618 == pim_ifp
->pim_neighbors_highest_override_interval_msec
) {
619 /* then find the next highest propagation delay */
620 pim_ifp
->pim_neighbors_highest_override_interval_msec
=
621 find_neighbors_next_highest_override_interval_msec(
626 if (PIM_DEBUG_PIM_TRACE
) {
627 zlog_debug("%s: deleting PIM neighbor %pPA on interface %s",
628 __func__
, &neigh
->source_addr
, ifp
->name
);
631 listnode_delete(pim_ifp
->pim_neighbor_list
, neigh
);
633 pim_neighbor_free(neigh
);
635 sched_rpf_cache_refresh(pim_ifp
->pim
);
638 void pim_neighbor_delete_all(struct interface
*ifp
, const char *delete_message
)
640 struct pim_interface
*pim_ifp
;
641 struct listnode
*neigh_node
;
642 struct listnode
*neigh_nextnode
;
643 struct pim_neighbor
*neigh
;
648 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_neighbor_list
, neigh_node
,
649 neigh_nextnode
, neigh
)) {
650 pim_neighbor_delete(ifp
, neigh
, delete_message
);
654 struct prefix
*pim_neighbor_find_secondary(struct pim_neighbor
*neigh
,
657 struct listnode
*node
;
660 if (!neigh
->prefix_list
)
663 for (ALL_LIST_ELEMENTS_RO(neigh
->prefix_list
, node
, p
)) {
664 if (prefix_same(p
, addr
))
672 RFC 4601: 4.3.4. Maintaining Secondary Address Lists
674 All the advertised secondary addresses in received Hello messages
675 must be checked against those previously advertised by all other
676 PIM neighbors on that interface. If there is a conflict and the
677 same secondary address was previously advertised by another
678 neighbor, then only the most recently received mapping MUST be
679 maintained, and an error message SHOULD be logged to the
680 administrator in a rate-limited manner.
682 static void delete_from_neigh_addr(struct interface
*ifp
,
683 struct list
*addr_list
, pim_addr neigh_addr
)
685 struct listnode
*addr_node
;
687 struct pim_interface
*pim_ifp
;
695 Scan secondary address list
697 for (ALL_LIST_ELEMENTS_RO(addr_list
, addr_node
, addr
)) {
698 struct listnode
*neigh_node
;
699 struct pim_neighbor
*neigh
;
701 if (addr
->family
!= PIM_AF
)
706 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
,
707 neigh_node
, neigh
)) {
709 struct prefix
*p
= pim_neighbor_find_secondary(
713 "secondary addr %pFXh recvd from neigh %pPA deleted from neigh %pPA on %s",
715 &neigh
->source_addr
, ifp
->name
);
717 listnode_delete(neigh
->prefix_list
, p
);
722 } /* scan neighbors */
724 } /* scan addr list */
727 void pim_neighbor_update(struct pim_neighbor
*neigh
,
728 pim_hello_options hello_options
, uint16_t holdtime
,
729 uint32_t dr_priority
, struct list
*addr_list
)
731 struct pim_interface
*pim_ifp
= neigh
->interface
->info
;
734 /* Received holdtime ? */
735 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
736 pim_neighbor_timer_reset(neigh
, holdtime
);
738 pim_neighbor_timer_reset(neigh
,
739 PIM_IF_DEFAULT_HOLDTIME(pim_ifp
));
742 #ifdef DUMP_PREFIX_LIST
744 "%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d",
745 __func__
, (unsigned)neigh
->prefix_list
,
746 neigh
->prefix_list
? (int)listcount(neigh
->prefix_list
) : -1,
748 addr_list
? (int)listcount(addr_list
) : -1);
751 if (neigh
->prefix_list
== addr_list
) {
755 "%s: internal error: trying to replace same prefix list=%p",
756 __func__
, (void *)addr_list
);
759 /* Delete existing secondary address list */
760 delete_prefix_list(neigh
);
764 delete_from_neigh_addr(neigh
->interface
, addr_list
,
768 /* Replace secondary address list */
769 neigh
->prefix_list
= addr_list
;
771 update_dr_priority(neigh
, hello_options
, dr_priority
);
772 new = PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_LAN_PRUNE_DELAY
);
773 old
= PIM_OPTION_IS_SET(neigh
->hello_options
,
774 PIM_OPTION_MASK_LAN_PRUNE_DELAY
);
778 ++pim_ifp
->pim_number_of_nonlandelay_neighbors
;
780 --pim_ifp
->pim_number_of_nonlandelay_neighbors
;
785 neigh
->hello_options
= hello_options
;