3 * Copyright (C) 2008 Everton da Silva Marques
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
38 #include "pim_iface.h"
40 #include "pim_zlookup.h"
41 #include "pim_upstream.h"
42 #include "pim_ifchannel.h"
43 #include "pim_neighbor.h"
45 #include "pim_zebra.h"
47 #include "pim_macro.h"
50 #include "pim_register.h"
52 #include "pim_jp_agg.h"
55 #include "pim_vxlan.h"
58 static void join_timer_stop(struct pim_upstream
*up
);
60 pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
);
61 static bool pim_upstream_sg_running_proc(struct pim_upstream
*up
);
64 * A (*,G) or a (*,*) is going away
65 * remove the parent pointer from
66 * those pointing at us
68 static void pim_upstream_remove_children(struct pim_instance
*pim
,
69 struct pim_upstream
*up
)
71 struct pim_upstream
*child
;
76 while (!list_isempty(up
->sources
)) {
77 child
= listnode_head(up
->sources
);
78 listnode_delete(up
->sources
, child
);
79 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child
->flags
)) {
80 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child
->flags
);
81 child
= pim_upstream_del(pim
, child
, __func__
);
85 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child
->flags
))
86 pim_upstream_mroute_iif_update(
91 list_delete(&up
->sources
);
95 * A (*,G) or a (*,*) is being created
96 * Find the children that would point
99 static void pim_upstream_find_new_children(struct pim_instance
*pim
,
100 struct pim_upstream
*up
)
102 struct pim_upstream
*child
;
104 if (!pim_addr_is_any(up
->sg
.src
) && !pim_addr_is_any(up
->sg
.grp
))
107 if (pim_addr_is_any(up
->sg
.src
) && pim_addr_is_any(up
->sg
.grp
))
110 frr_each (rb_pim_upstream
, &pim
->upstream_head
, child
) {
111 if (!pim_addr_is_any(up
->sg
.grp
) &&
112 !pim_addr_cmp(child
->sg
.grp
, up
->sg
.grp
) && (child
!= up
)) {
114 listnode_add_sort(up
->sources
, child
);
115 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child
->flags
))
116 pim_upstream_mroute_iif_update(
124 * If we have a (*,*) || (S,*) there is no parent
125 * If we have a (S,G), find the (*,G)
126 * If we have a (*,G), find the (*,*)
128 static struct pim_upstream
*pim_upstream_find_parent(struct pim_instance
*pim
,
129 struct pim_upstream
*child
)
131 pim_sgaddr any
= child
->sg
;
132 struct pim_upstream
*up
= NULL
;
135 if (!pim_addr_is_any(child
->sg
.src
) &&
136 !pim_addr_is_any(child
->sg
.grp
)) {
137 any
.src
= PIMADDR_ANY
;
138 up
= pim_upstream_find(pim
, &any
);
141 listnode_add(up
->sources
, child
);
144 * In case parent is MLAG entry copy the data to child
146 if (up
&& PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
)) {
147 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(child
->flags
);
148 if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->flags
))
149 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(child
->flags
);
151 PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(
161 static void upstream_channel_oil_detach(struct pim_upstream
*up
)
163 struct channel_oil
*channel_oil
= up
->channel_oil
;
166 /* Detaching from channel_oil, channel_oil may exist post del,
167 but upstream would not keep reference of it
169 channel_oil
->up
= NULL
;
170 up
->channel_oil
= NULL
;
172 /* attempt to delete channel_oil; if channel_oil is being held
173 * because of other references cleanup info such as "Mute"
174 * inferred from the parent upstream
176 pim_channel_oil_upstream_deref(channel_oil
);
181 static void pim_upstream_timers_stop(struct pim_upstream
*up
)
183 THREAD_OFF(up
->t_ka_timer
);
184 THREAD_OFF(up
->t_rs_timer
);
185 THREAD_OFF(up
->t_msdp_reg_timer
);
186 THREAD_OFF(up
->t_join_timer
);
189 struct pim_upstream
*pim_upstream_del(struct pim_instance
*pim
,
190 struct pim_upstream
*up
, const char *name
)
192 struct listnode
*node
, *nnode
;
193 struct pim_ifchannel
*ch
;
194 bool notify_msdp
= false;
197 if (PIM_DEBUG_PIM_TRACE
)
199 "%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
200 __func__
, name
, up
->sg_str
, pim
->vrf
->name
,
201 up
->ref_count
, up
->flags
,
202 up
->channel_oil
->oil_ref_count
);
204 assert(up
->ref_count
> 0);
208 if (up
->ref_count
>= 1)
212 zlog_debug("pim_upstream free vrf:%s %s flags 0x%x",
213 pim
->vrf
->name
, up
->sg_str
, up
->flags
);
215 if (pim_up_mlag_is_local(up
))
216 pim_mlag_up_local_del(pim
, up
);
218 pim_upstream_timers_stop(up
);
220 if (up
->join_state
== PIM_UPSTREAM_JOINED
) {
221 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0);
223 if (pim_addr_is_any(up
->sg
.src
)) {
224 /* if a (*, G) entry in the joined state is being
226 * need to notify MSDP */
232 pim_jp_agg_upstream_verification(up
, false);
233 up
->rpf
.source_nexthop
.interface
= NULL
;
235 if (!pim_addr_is_any(up
->sg
.src
)) {
236 if (pim
->upstream_sg_wheel
)
237 wheel_remove_item(pim
->upstream_sg_wheel
, up
);
241 pim_mroute_del(up
->channel_oil
, __func__
);
242 upstream_channel_oil_detach(up
);
244 for (ALL_LIST_ELEMENTS(up
->ifchannels
, node
, nnode
, ch
))
245 pim_ifchannel_delete(ch
);
246 list_delete(&up
->ifchannels
);
248 pim_upstream_remove_children(pim
, up
);
250 list_delete(&up
->sources
);
252 if (up
->parent
&& up
->parent
->sources
)
253 listnode_delete(up
->parent
->sources
, up
);
256 rb_pim_upstream_del(&pim
->upstream_head
, up
);
259 pim_msdp_up_del(pim
, &up
->sg
);
262 /* When RP gets deleted, pim_rp_del() deregister addr with Zebra NHT
263 * and assign up->upstream_addr as INADDR_ANY.
264 * So before de-registering the upstream address, check if is not equal
265 * to INADDR_ANY. This is done in order to avoid de-registering for
266 * 255.255.255.255 which is maintained for some reason..
268 if (!pim_addr_is_any(up
->upstream_addr
)) {
269 /* Deregister addr with Zebra NHT */
270 pim_addr_to_prefix(&nht_p
, up
->upstream_addr
);
271 if (PIM_DEBUG_PIM_TRACE
)
273 "%s: Deregister upstream %s addr %pFX with Zebra NHT",
274 __func__
, up
->sg_str
, &nht_p
);
275 pim_delete_tracked_nexthop(pim
, &nht_p
, up
, NULL
);
278 XFREE(MTYPE_PIM_UPSTREAM
, up
);
283 void pim_upstream_send_join(struct pim_upstream
*up
)
285 if (!up
->rpf
.source_nexthop
.interface
) {
286 if (PIM_DEBUG_PIM_TRACE
)
287 zlog_debug("%s: up %s RPF is not present", __func__
,
292 if (PIM_DEBUG_PIM_TRACE
) {
293 char rpf_str
[PREFIX_STRLEN
];
294 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
296 zlog_debug("%s: RPF'%s=%s(%s) for Interface %s", __func__
,
298 pim_upstream_state2str(up
->join_state
),
299 up
->rpf
.source_nexthop
.interface
->name
);
300 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
301 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
302 __func__
, up
->sg_str
, rpf_str
);
307 /* send Join(S,G) to the current upstream neighbor */
308 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 1 /* join */);
311 static void on_join_timer(struct thread
*t
)
313 struct pim_upstream
*up
;
317 if (!up
->rpf
.source_nexthop
.interface
) {
318 if (PIM_DEBUG_PIM_TRACE
)
319 zlog_debug("%s: up %s RPF is not present", __func__
,
325 * In the case of a HFR we will not ahve anyone to send this to.
327 if (PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
331 * Don't send the join if the outgoing interface is a loopback
332 * But since this might change leave the join timer running
334 if (up
->rpf
.source_nexthop
335 .interface
&& !if_is_loopback(up
->rpf
.source_nexthop
.interface
))
336 pim_upstream_send_join(up
);
338 join_timer_start(up
);
341 static void join_timer_stop(struct pim_upstream
*up
)
343 struct pim_neighbor
*nbr
= NULL
;
345 THREAD_OFF(up
->t_join_timer
);
347 if (up
->rpf
.source_nexthop
.interface
)
348 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
352 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
, nbr
);
354 pim_jp_agg_upstream_verification(up
, false);
357 void join_timer_start(struct pim_upstream
*up
)
359 struct pim_neighbor
*nbr
= NULL
;
361 if (up
->rpf
.source_nexthop
.interface
) {
362 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
365 if (PIM_DEBUG_PIM_EVENTS
) {
367 "%s: starting %d sec timer for upstream (S,G)=%s",
368 __func__
, router
->t_periodic
, up
->sg_str
);
373 pim_jp_agg_add_group(nbr
->upstream_jp_agg
, up
, 1, nbr
);
375 THREAD_OFF(up
->t_join_timer
);
376 thread_add_timer(router
->master
, on_join_timer
, up
,
377 router
->t_periodic
, &up
->t_join_timer
);
379 pim_jp_agg_upstream_verification(up
, true);
383 * This is only called when we are switching the upstream
384 * J/P from one neighbor to another
386 * As such we need to remove from the old list and
387 * add to the new list.
389 void pim_upstream_join_timer_restart(struct pim_upstream
*up
,
392 // THREAD_OFF(up->t_join_timer);
393 join_timer_start(up
);
396 static void pim_upstream_join_timer_restart_msec(struct pim_upstream
*up
,
399 if (PIM_DEBUG_PIM_EVENTS
) {
400 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
401 __func__
, interval_msec
, up
->sg_str
);
404 THREAD_OFF(up
->t_join_timer
);
405 thread_add_timer_msec(router
->master
, on_join_timer
, up
, interval_msec
,
409 void pim_update_suppress_timers(uint32_t suppress_time
)
411 struct pim_instance
*pim
;
413 unsigned int old_rp_ka_time
;
415 /* stash the old one so we know which values were manually configured */
416 old_rp_ka_time
= (3 * router
->register_suppress_time
417 + router
->register_probe_time
);
418 router
->register_suppress_time
= suppress_time
;
420 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
425 /* Only adjust if not manually configured */
426 if (pim
->rp_keep_alive_time
== old_rp_ka_time
)
427 pim
->rp_keep_alive_time
= PIM_RP_KEEPALIVE_PERIOD
;
431 void pim_upstream_join_suppress(struct pim_upstream
*up
, struct prefix rpf
,
434 long t_joinsuppress_msec
;
435 long join_timer_remain_msec
= 0;
436 struct pim_neighbor
*nbr
= NULL
;
438 if (!up
->rpf
.source_nexthop
.interface
) {
439 if (PIM_DEBUG_PIM_TRACE
)
440 zlog_debug("%s: up %s RPF is not present", __func__
,
445 t_joinsuppress_msec
=
446 MIN(pim_if_t_suppressed_msec(up
->rpf
.source_nexthop
.interface
),
449 if (up
->t_join_timer
)
450 join_timer_remain_msec
=
451 pim_time_timer_remain_msec(up
->t_join_timer
);
453 /* Remove it from jp agg from the nbr for suppression */
454 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
457 join_timer_remain_msec
=
458 pim_time_timer_remain_msec(nbr
->jp_timer
);
462 if (PIM_DEBUG_PIM_TRACE
) {
463 char rpf_str
[INET_ADDRSTRLEN
];
465 pim_addr_dump("<rpf?>", &rpf
, rpf_str
, sizeof(rpf_str
));
467 "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
468 __FILE__
, __func__
, up
->sg_str
, rpf_str
,
469 join_timer_remain_msec
, t_joinsuppress_msec
);
472 if (join_timer_remain_msec
< t_joinsuppress_msec
) {
473 if (PIM_DEBUG_PIM_TRACE
) {
475 "%s %s: suppressing Join(S,G)=%s for %ld msec",
476 __FILE__
, __func__
, up
->sg_str
,
477 t_joinsuppress_msec
);
481 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
, nbr
);
483 pim_upstream_join_timer_restart_msec(up
, t_joinsuppress_msec
);
487 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label
,
488 struct pim_upstream
*up
)
490 long join_timer_remain_msec
;
493 if (!up
->rpf
.source_nexthop
.interface
) {
494 if (PIM_DEBUG_PIM_TRACE
)
495 zlog_debug("%s: up %s RPF is not present", __func__
,
501 pim_if_t_override_msec(up
->rpf
.source_nexthop
.interface
);
503 if (up
->t_join_timer
) {
504 join_timer_remain_msec
=
505 pim_time_timer_remain_msec(up
->t_join_timer
);
507 /* upstream join tracked with neighbor jp timer */
508 struct pim_neighbor
*nbr
;
510 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
513 join_timer_remain_msec
=
514 pim_time_timer_remain_msec(nbr
->jp_timer
);
516 /* Manipulate such that override takes place */
517 join_timer_remain_msec
= t_override_msec
+ 1;
520 if (PIM_DEBUG_PIM_TRACE
) {
521 char rpf_str
[INET_ADDRSTRLEN
];
523 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
527 "%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
528 debug_label
, up
->sg_str
, rpf_str
,
529 join_timer_remain_msec
, t_override_msec
);
532 if (join_timer_remain_msec
> t_override_msec
) {
533 if (PIM_DEBUG_PIM_TRACE
) {
535 "%s: decreasing (S,G)=%s join timer to t_override=%d msec",
536 debug_label
, up
->sg_str
, t_override_msec
);
539 pim_upstream_join_timer_restart_msec(up
, t_override_msec
);
543 static void forward_on(struct pim_upstream
*up
)
545 struct listnode
*chnode
;
546 struct listnode
*chnextnode
;
547 struct pim_ifchannel
*ch
= NULL
;
549 /* scan (S,G) state */
550 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
551 if (pim_macro_chisin_oiflist(ch
))
552 pim_forward_start(ch
);
554 } /* scan iface channel list */
557 static void forward_off(struct pim_upstream
*up
)
559 struct listnode
*chnode
;
560 struct listnode
*chnextnode
;
561 struct pim_ifchannel
*ch
;
563 /* scan per-interface (S,G) state */
564 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
566 pim_forward_stop(ch
);
568 } /* scan iface channel list */
571 int pim_upstream_could_register(struct pim_upstream
*up
)
573 struct pim_interface
*pim_ifp
= NULL
;
575 /* FORCE_PIMREG is a generic flag to let an app like VxLAN-AA register
576 * a source on an upstream entry even if the source is not directly
577 * connected on the IIF.
579 if (PIM_UPSTREAM_FLAG_TEST_FORCE_PIMREG(up
->flags
))
582 if (up
->rpf
.source_nexthop
.interface
)
583 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
585 if (PIM_DEBUG_PIM_TRACE
)
586 zlog_debug("%s: up %s RPF is not present", __func__
,
590 if (pim_ifp
&& PIM_I_am_DR(pim_ifp
)
591 && pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
598 /* Source registration is suppressed for SSM groups. When the SSM range changes
599 * we re-revaluate register setup for existing upstream entries */
600 void pim_upstream_register_reevaluate(struct pim_instance
*pim
)
602 struct pim_upstream
*up
;
604 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
605 /* If FHR is set CouldRegister is True. Also check if the flow
606 * is actually active; if it is not kat setup will trigger
608 * registration whenever the flow becomes active. */
609 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) ||
610 !pim_upstream_is_kat_running(up
))
613 if (pim_is_grp_ssm(pim
, up
->sg
.grp
)) {
614 /* clear the register state for SSM groups */
615 if (up
->reg_state
!= PIM_REG_NOINFO
) {
616 if (PIM_DEBUG_PIM_EVENTS
)
618 "Clear register for %s as G is now SSM",
620 /* remove regiface from the OIL if it is there*/
621 pim_channel_del_oif(up
->channel_oil
,
623 PIM_OIF_FLAG_PROTO_PIM
,
625 up
->reg_state
= PIM_REG_NOINFO
;
628 /* register ASM sources with the RP */
629 if (up
->reg_state
== PIM_REG_NOINFO
) {
630 if (PIM_DEBUG_PIM_EVENTS
)
632 "Register %s as G is now ASM",
634 pim_channel_add_oif(up
->channel_oil
,
636 PIM_OIF_FLAG_PROTO_PIM
,
638 up
->reg_state
= PIM_REG_JOIN
;
644 /* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
646 * 1. along the SPT if SPTbit is set
647 * 2. and along the RPT if SPTbit is not set
648 * If forwarding is hw accelerated i.e. control and dataplane components
649 * are separate you may not be able to reliably set SPT bit on intermediate
650 * routers while still forwarding on the (S,G,rpt).
652 * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
653 * criteria to decide between using the RPT vs. SPT for forwarding.
655 void pim_upstream_update_use_rpt(struct pim_upstream
*up
,
661 if (pim_addr_is_any(up
->sg
.src
))
664 old_use_rpt
= !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
666 /* We will use the SPT (IIF=RPF_interface(S) if -
667 * 1. We have decided to join the SPT
669 * 3. Source is directly connected
670 * 4. We are RP (parent's IIF is lo or vrf-device)
671 * In all other cases the source will stay along the RPT and
672 * IIF=RPF_interface(RP).
674 if (up
->join_state
== PIM_UPSTREAM_JOINED
||
675 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) ||
676 pim_if_connected_to_source(
677 up
->rpf
.source_nexthop
.interface
,
679 /* XXX - need to switch this to a more efficient
682 I_am_RP(up
->pim
, up
->sg
.grp
))
684 PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up
->flags
);
687 PIM_UPSTREAM_FLAG_SET_USE_RPT(up
->flags
);
689 new_use_rpt
= !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
690 if (old_use_rpt
!= new_use_rpt
) {
691 if (PIM_DEBUG_PIM_EVENTS
)
692 zlog_debug("%s switched from %s to %s",
694 old_use_rpt
?"RPT":"SPT",
695 new_use_rpt
?"RPT":"SPT");
697 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
701 /* some events like RP change require re-evaluation of SGrpt across
704 void pim_upstream_reeval_use_rpt(struct pim_instance
*pim
)
706 struct pim_upstream
*up
;
708 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
709 if (pim_addr_is_any(up
->sg
.src
))
712 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
716 void pim_upstream_switch(struct pim_instance
*pim
, struct pim_upstream
*up
,
717 enum pim_upstream_state new_state
)
719 enum pim_upstream_state old_state
= up
->join_state
;
721 if (pim_addr_is_any(up
->upstream_addr
)) {
722 if (PIM_DEBUG_PIM_EVENTS
)
723 zlog_debug("%s: RPF not configured for %s", __func__
,
728 if (!up
->rpf
.source_nexthop
.interface
) {
729 if (PIM_DEBUG_PIM_EVENTS
)
730 zlog_debug("%s: RP not reachable for %s", __func__
,
735 if (PIM_DEBUG_PIM_EVENTS
) {
736 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
737 __func__
, up
->sg_str
,
738 pim_upstream_state2str(up
->join_state
),
739 pim_upstream_state2str(new_state
));
742 up
->join_state
= new_state
;
743 if (old_state
!= new_state
)
744 up
->state_transition
= pim_time_monotonic_sec();
746 pim_upstream_update_assert_tracking_desired(up
);
748 if (new_state
== PIM_UPSTREAM_JOINED
) {
749 pim_upstream_inherited_olist_decide(pim
, up
);
750 if (old_state
!= PIM_UPSTREAM_JOINED
) {
751 int old_fhr
= PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
);
753 pim_msdp_up_join_state_changed(pim
, up
);
754 if (pim_upstream_could_register(up
)) {
755 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
757 && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(
759 pim_upstream_keep_alive_timer_start(
760 up
, pim
->keep_alive_time
);
761 pim_register_join(up
);
764 pim_upstream_send_join(up
);
765 join_timer_start(up
);
768 if (old_state
!= new_state
)
769 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
773 bool send_xg_jp
= false;
777 * RFC 4601 Sec 4.5.7:
778 * JoinDesired(S,G) -> False, set SPTbit to false.
780 if (!pim_addr_is_any(up
->sg
.src
))
781 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
783 if (old_state
== PIM_UPSTREAM_JOINED
)
784 pim_msdp_up_join_state_changed(pim
, up
);
786 if (old_state
!= new_state
) {
788 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
789 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
791 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
793 (new_use_rpt
!= old_use_rpt
) &&
795 /* we have decided to switch from the SPT back
796 * to the RPT which means we need to cancel
797 * any previously sent SGrpt prunes immediately
802 /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
804 If I am RP for G then send S,G prune to its IIF. */
805 if (pim_upstream_is_sg_rpt(up
) && up
->parent
&&
806 !I_am_RP(pim
, up
->sg
.grp
))
809 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0 /* prune */);
812 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
814 "re-join RPT; *,G IIF %s S,G IIF %s ",
815 up
->parent
->rpf
.source_nexthop
.interface
?
816 up
->parent
->rpf
.source_nexthop
.interface
->name
818 up
->rpf
.source_nexthop
.interface
?
819 up
->rpf
.source_nexthop
.interface
->name
:
821 pim_jp_agg_single_upstream_send(&up
->parent
->rpf
,
829 int pim_upstream_compare(const struct pim_upstream
*up1
,
830 const struct pim_upstream
*up2
)
832 return pim_sgaddr_cmp(up1
->sg
, up2
->sg
);
835 void pim_upstream_fill_static_iif(struct pim_upstream
*up
,
836 struct interface
*incoming
)
838 up
->rpf
.source_nexthop
.interface
= incoming
;
840 /* reset other parameters to matched a connected incoming interface */
841 pim_addr_to_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
,
843 up
->rpf
.source_nexthop
.mrib_metric_preference
=
844 ZEBRA_CONNECT_DISTANCE_DEFAULT
;
845 up
->rpf
.source_nexthop
.mrib_route_metric
= 0;
846 up
->rpf
.rpf_addr
.family
= AF_INET
;
847 up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
= PIM_NET_INADDR_ANY
;
851 static struct pim_upstream
*pim_upstream_new(struct pim_instance
*pim
,
853 struct interface
*incoming
,
855 struct pim_ifchannel
*ch
)
857 enum pim_rpf_result rpf_result
;
858 struct pim_interface
*pim_ifp
;
859 struct pim_upstream
*up
;
861 up
= XCALLOC(MTYPE_PIM_UPSTREAM
, sizeof(*up
));
865 snprintfrr(up
->sg_str
, sizeof(up
->sg_str
), "%pSG", sg
);
869 rb_pim_upstream_add(&pim
->upstream_head
, up
);
870 /* Set up->upstream_addr as INADDR_ANY, if RP is not
871 * configured and retain the upstream data structure
873 if (!pim_rp_set_upstream_addr(pim
, &up
->upstream_addr
, sg
->src
,
875 if (PIM_DEBUG_PIM_TRACE
)
876 zlog_debug("%s: Received a (*,G) with no RP configured",
880 up
->parent
= pim_upstream_find_parent(pim
, up
);
881 if (pim_addr_is_any(up
->sg
.src
)) {
882 up
->sources
= list_new();
884 (int (*)(void *, void *))pim_upstream_compare
;
888 pim_upstream_find_new_children(pim
, up
);
891 up
->t_join_timer
= NULL
;
892 up
->t_ka_timer
= NULL
;
893 up
->t_rs_timer
= NULL
;
894 up
->t_msdp_reg_timer
= NULL
;
895 up
->join_state
= PIM_UPSTREAM_NOTJOINED
;
896 up
->reg_state
= PIM_REG_NOINFO
;
897 up
->state_transition
= pim_time_monotonic_sec();
898 up
->channel_oil
= pim_channel_oil_add(pim
, &up
->sg
, __func__
);
899 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
901 up
->rpf
.source_nexthop
.interface
= NULL
;
902 pim_addr_to_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
,
904 up
->rpf
.source_nexthop
.mrib_metric_preference
=
905 router
->infinite_assert_metric
.metric_preference
;
906 up
->rpf
.source_nexthop
.mrib_route_metric
=
907 router
->infinite_assert_metric
.route_metric
;
908 pim_addr_to_prefix(&up
->rpf
.rpf_addr
, PIMADDR_ANY
);
909 up
->ifchannels
= list_new();
910 up
->ifchannels
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
912 if (!pim_addr_is_any(up
->sg
.src
)) {
913 wheel_add_item(pim
->upstream_sg_wheel
, up
);
915 /* Inherit the DF role from the parent (*, G) entry for
919 && PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up
->parent
->flags
)
920 && PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->parent
->flags
)) {
921 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up
->flags
);
924 "upstream %s inherited mlag non-df flag from parent",
929 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
)
930 || PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
)) {
931 pim_upstream_fill_static_iif(up
, incoming
);
932 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
934 pim_upstream_update_use_rpt(up
,
935 false /*update_mroute*/);
936 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
938 if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
))
939 pim_upstream_keep_alive_timer_start(
940 up
, pim
->keep_alive_time
);
941 } else if (!pim_addr_is_any(up
->upstream_addr
)) {
942 pim_upstream_update_use_rpt(up
,
943 false /*update_mroute*/);
944 rpf_result
= pim_rpf_update(pim
, up
, NULL
, __func__
);
945 if (rpf_result
== PIM_RPF_FAILURE
) {
946 if (PIM_DEBUG_PIM_TRACE
)
948 "%s: Attempting to create upstream(%s), Unable to RPF for source",
949 __func__
, up
->sg_str
);
952 if (up
->rpf
.source_nexthop
.interface
) {
953 pim_upstream_mroute_iif_update(up
->channel_oil
,
958 /* send the entry to the MLAG peer */
959 /* XXX - duplicate send is possible here if pim_rpf_update
960 * successfully resolved the nexthop
962 if (pim_up_mlag_is_local(up
)
963 || PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
))
964 pim_mlag_up_local_add(pim
, up
);
966 if (PIM_DEBUG_PIM_TRACE
) {
968 "%s: Created Upstream %s upstream_addr %pPAs ref count %d increment",
969 __func__
, up
->sg_str
, &up
->upstream_addr
,
976 uint32_t pim_up_mlag_local_cost(struct pim_upstream
*up
)
978 if (!(pim_up_mlag_is_local(up
))
979 && !(up
->flags
& PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE
))
980 return router
->infinite_assert_metric
.route_metric
;
982 if ((up
->rpf
.source_nexthop
.interface
==
983 up
->pim
->vxlan
.peerlink_rif
) &&
984 (up
->rpf
.source_nexthop
.mrib_route_metric
<
985 (router
->infinite_assert_metric
.route_metric
-
986 PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC
)))
987 return up
->rpf
.source_nexthop
.mrib_route_metric
+
988 PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC
;
990 return up
->rpf
.source_nexthop
.mrib_route_metric
;
993 uint32_t pim_up_mlag_peer_cost(struct pim_upstream
*up
)
995 if (!(up
->flags
& PIM_UPSTREAM_FLAG_MASK_MLAG_PEER
))
996 return router
->infinite_assert_metric
.route_metric
;
998 return up
->mlag
.peer_mrib_metric
;
1001 struct pim_upstream
*pim_upstream_find(struct pim_instance
*pim
, pim_sgaddr
*sg
)
1003 struct pim_upstream lookup
;
1004 struct pim_upstream
*up
= NULL
;
1007 up
= rb_pim_upstream_find(&pim
->upstream_head
, &lookup
);
1011 struct pim_upstream
*pim_upstream_find_or_add(pim_sgaddr
*sg
,
1012 struct interface
*incoming
,
1013 int flags
, const char *name
)
1015 struct pim_interface
*pim_ifp
= incoming
->info
;
1017 return (pim_upstream_add(pim_ifp
->pim
, sg
, incoming
, flags
, name
,
1021 void pim_upstream_ref(struct pim_upstream
*up
, int flags
, const char *name
)
1023 /* if a local MLAG reference is being created we need to send the mroute
1026 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up
->flags
) &&
1027 PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags
)) {
1028 PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(up
->flags
);
1029 pim_mlag_up_local_add(up
->pim
, up
);
1032 /* when we go from non-FHR to FHR we need to re-eval traffic
1035 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) &&
1036 PIM_UPSTREAM_FLAG_TEST_FHR(flags
)) {
1037 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1038 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
1041 /* re-eval joinDesired; clearing peer-msdp-sa flag can
1042 * cause JD to change
1044 if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up
->flags
) &&
1045 PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags
)) {
1046 PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up
->flags
);
1047 pim_upstream_update_join_desired(up
->pim
, up
);
1052 if (PIM_DEBUG_PIM_TRACE
)
1053 zlog_debug("%s(%s): upstream %s ref count %d increment",
1054 __func__
, name
, up
->sg_str
, up
->ref_count
);
1057 struct pim_upstream
*pim_upstream_add(struct pim_instance
*pim
, pim_sgaddr
*sg
,
1058 struct interface
*incoming
, int flags
,
1060 struct pim_ifchannel
*ch
)
1062 struct pim_upstream
*up
= NULL
;
1065 up
= pim_upstream_find(pim
, sg
);
1067 pim_upstream_ref(up
, flags
, name
);
1070 up
= pim_upstream_new(pim
, sg
, incoming
, flags
, ch
);
1073 if (PIM_DEBUG_PIM_TRACE
) {
1075 zlog_debug("%s(%s): %s, iif %pFX (%s) found: %d: ref_count: %d",
1077 up
->sg_str
, &up
->rpf
.rpf_addr
, up
->rpf
.source_nexthop
.interface
?
1078 up
->rpf
.source_nexthop
.interface
->name
: "Unknown" ,
1079 found
, up
->ref_count
);
1081 zlog_debug("%s(%s): (%pSG) failure to create", __func__
,
1089 * Passed in up must be the upstream for ch. starch is NULL if no
1091 * This function is copied over from
1092 * pim_upstream_evaluate_join_desired_interface but limited to
1093 * parent (*,G)'s includes/joins.
1095 int pim_upstream_eval_inherit_if(struct pim_upstream
*up
,
1096 struct pim_ifchannel
*ch
,
1097 struct pim_ifchannel
*starch
)
1099 /* if there is an explicit prune for this interface we cannot
1103 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1107 /* Check if the OIF can be inherited fron the (*,G) entry
1110 if (!pim_macro_ch_lost_assert(starch
)
1111 && pim_macro_chisin_joins_or_include(starch
))
1119 * Passed in up must be the upstream for ch. starch is NULL if no
1122 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream
*up
,
1123 struct pim_ifchannel
*ch
,
1124 struct pim_ifchannel
*starch
)
1127 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1130 if (!pim_macro_ch_lost_assert(ch
)
1131 && pim_macro_chisin_joins_or_include(ch
))
1139 /* XXX: check on this with donald
1140 * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in
1144 if (PIM_IF_FLAG_TEST_S_G_RPT(starch
->upstream
->flags
))
1148 if (!pim_macro_ch_lost_assert(starch
)
1149 && pim_macro_chisin_joins_or_include(starch
))
1156 /* Returns true if immediate OIL is empty and is used to evaluate
1157 * JoinDesired. See pim_upstream_evaluate_join_desired.
1159 static bool pim_upstream_empty_immediate_olist(struct pim_instance
*pim
,
1160 struct pim_upstream
*up
)
1162 struct interface
*ifp
;
1163 struct pim_ifchannel
*ch
;
1165 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1169 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1173 /* If we have even one immediate OIF we can return with
1176 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1179 } /* scan iface channel list */
1181 /* immediate_oil is empty */
1186 static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream
*up
)
1188 return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up
->flags
);
1192 * bool JoinDesired(*,G) {
1193 * if (immediate_olist(*,G) != NULL)
1199 * bool JoinDesired(S,G) {
1200 * return( immediate_olist(S,G) != NULL
1201 * OR ( KeepaliveTimer(S,G) is running
1202 * AND inherited_olist(S,G) != NULL ) )
1205 bool pim_upstream_evaluate_join_desired(struct pim_instance
*pim
,
1206 struct pim_upstream
*up
)
1211 empty_imm_oil
= pim_upstream_empty_immediate_olist(pim
, up
);
1214 if (pim_addr_is_any(up
->sg
.src
))
1215 return !empty_imm_oil
;
1220 empty_inh_oil
= pim_upstream_empty_inherited_olist(up
);
1221 if (!empty_inh_oil
&&
1222 (pim_upstream_is_kat_running(up
) ||
1223 pim_upstream_is_msdp_peer_sa(up
)))
1230 See also pim_upstream_evaluate_join_desired() above.
1232 void pim_upstream_update_join_desired(struct pim_instance
*pim
,
1233 struct pim_upstream
*up
)
1235 int was_join_desired
; /* boolean */
1236 int is_join_desired
; /* boolean */
1238 was_join_desired
= PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up
->flags
);
1240 is_join_desired
= pim_upstream_evaluate_join_desired(pim
, up
);
1241 if (is_join_desired
)
1242 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up
->flags
);
1244 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up
->flags
);
1246 /* switched from false to true */
1247 if (is_join_desired
&& (up
->join_state
== PIM_UPSTREAM_NOTJOINED
)) {
1248 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_JOINED
);
1252 /* switched from true to false */
1253 if (!is_join_desired
&& was_join_desired
) {
1254 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
1260 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
1261 Transitions from Joined State
1262 RPF'(S,G) GenID changes
1264 The upstream (S,G) state machine remains in Joined state. If the
1265 Join Timer is set to expire in more than t_override seconds, reset
1266 it so that it expires after t_override seconds.
1268 void pim_upstream_rpf_genid_changed(struct pim_instance
*pim
,
1269 pim_addr neigh_addr
)
1271 struct pim_upstream
*up
;
1274 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1276 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
1279 rpf_addr
= pim_addr_from_prefix(&up
->rpf
.rpf_addr
);
1281 if (PIM_DEBUG_PIM_TRACE
)
1283 "%s: matching neigh=%pPA against upstream (S,G)=%s[%s] joined=%d rpf_addr=%pPA",
1284 __func__
, &neigh_addr
, up
->sg_str
,
1286 up
->join_state
== PIM_UPSTREAM_JOINED
,
1289 /* consider only (S,G) upstream in Joined state */
1290 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
1293 /* match RPF'(S,G)=neigh_addr */
1294 if (pim_addr_cmp(rpf_addr
, neigh_addr
))
1297 pim_upstream_join_timer_decrease_to_t_override(
1298 "RPF'(S,G) GenID change", up
);
1303 void pim_upstream_rpf_interface_changed(struct pim_upstream
*up
,
1304 struct interface
*old_rpf_ifp
)
1306 struct listnode
*chnode
;
1307 struct listnode
*chnextnode
;
1308 struct pim_ifchannel
*ch
;
1310 /* search all ifchannels */
1311 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1312 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1314 /* RPF_interface(S) was NOT I */
1315 (old_rpf_ifp
== ch
->interface
) &&
1316 /* RPF_interface(S) stopped being I */
1317 (ch
->upstream
->rpf
.source_nexthop
1319 (ch
->upstream
->rpf
.source_nexthop
1320 .interface
!= ch
->interface
)) {
1321 assert_action_a5(ch
);
1323 } /* PIM_IFASSERT_I_AM_LOSER */
1325 pim_ifchannel_update_assert_tracking_desired(ch
);
1329 void pim_upstream_update_could_assert(struct pim_upstream
*up
)
1331 struct listnode
*chnode
;
1332 struct listnode
*chnextnode
;
1333 struct pim_ifchannel
*ch
;
1335 /* scan per-interface (S,G) state */
1336 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1337 pim_ifchannel_update_could_assert(ch
);
1338 } /* scan iface channel list */
1341 void pim_upstream_update_my_assert_metric(struct pim_upstream
*up
)
1343 struct listnode
*chnode
;
1344 struct listnode
*chnextnode
;
1345 struct pim_ifchannel
*ch
;
1347 /* scan per-interface (S,G) state */
1348 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1349 pim_ifchannel_update_my_assert_metric(ch
);
1351 } /* scan iface channel list */
1354 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
)
1356 struct listnode
*chnode
;
1357 struct listnode
*chnextnode
;
1358 struct pim_interface
*pim_ifp
;
1359 struct pim_ifchannel
*ch
;
1361 /* scan per-interface (S,G) state */
1362 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1365 pim_ifp
= ch
->interface
->info
;
1369 pim_ifchannel_update_assert_tracking_desired(ch
);
1371 } /* scan iface channel list */
1374 /* When kat is stopped CouldRegister goes to false so we need to
1375 * transition the (S, G) on FHR to NI state and remove reg tunnel
1377 static void pim_upstream_fhr_kat_expiry(struct pim_instance
*pim
,
1378 struct pim_upstream
*up
)
1380 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
1383 if (PIM_DEBUG_PIM_TRACE
)
1384 zlog_debug("kat expired on %s; clear fhr reg state",
1387 /* stop reg-stop timer */
1388 THREAD_OFF(up
->t_rs_timer
);
1389 /* remove regiface from the OIL if it is there*/
1390 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
1391 PIM_OIF_FLAG_PROTO_PIM
, __func__
);
1392 /* clear the register state */
1393 up
->reg_state
= PIM_REG_NOINFO
;
1394 PIM_UPSTREAM_FLAG_UNSET_FHR(up
->flags
);
1397 /* When kat is started CouldRegister can go to true. And if it does we
1398 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1400 static void pim_upstream_fhr_kat_start(struct pim_upstream
*up
)
1402 if (pim_upstream_could_register(up
)) {
1403 if (PIM_DEBUG_PIM_TRACE
)
1405 "kat started on %s; set fhr reg state to joined",
1408 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1409 if (up
->reg_state
== PIM_REG_NOINFO
)
1410 pim_register_join(up
);
1411 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
1416 * On an RP, the PMBR value must be cleared when the
1417 * Keepalive Timer expires
1418 * KAT expiry indicates that flow is inactive. If the flow was created or
1419 * maintained by activity now is the time to deref it.
1421 struct pim_upstream
*pim_upstream_keep_alive_timer_proc(
1422 struct pim_upstream
*up
)
1424 struct pim_instance
*pim
;
1426 pim
= up
->channel_oil
->pim
;
1428 if (PIM_UPSTREAM_FLAG_TEST_DISABLE_KAT_EXPIRY(up
->flags
)) {
1429 /* if the router is a PIM vxlan encapsulator we prevent expiry
1430 * of KAT as the mroute is pre-setup without any traffic
1432 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
1436 if (I_am_RP(pim
, up
->sg
.grp
)) {
1437 pim_br_clear_pmbr(&up
->sg
);
1439 * We need to do more here :)
1440 * But this is the start.
1444 /* source is no longer active - pull the SA from MSDP's cache */
1445 pim_msdp_sa_local_del(pim
, &up
->sg
);
1447 /* JoinDesired can change when KAT is started or stopped */
1448 pim_upstream_update_join_desired(pim
, up
);
1450 /* if entry was created because of activity we need to deref it */
1451 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1452 pim_upstream_fhr_kat_expiry(pim
, up
);
1453 if (PIM_DEBUG_PIM_TRACE
)
1455 "kat expired on %s[%s]; remove stream reference",
1456 up
->sg_str
, pim
->vrf
->name
);
1457 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up
->flags
);
1459 /* Return if upstream entry got deleted.*/
1460 if (!pim_upstream_del(pim
, up
, __func__
))
1463 if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
)) {
1464 PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(up
->flags
);
1466 if (!pim_upstream_del(pim
, up
, __func__
))
1470 /* upstream reference would have been added to track the local
1471 * membership if it is LHR. We have to clear it when KAT expires.
1472 * Otherwise would result in stale entry with uncleared ref count.
1474 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
1475 struct pim_upstream
*parent
= up
->parent
;
1477 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up
->flags
);
1478 up
= pim_upstream_del(pim
, up
, __func__
);
1481 pim_jp_agg_single_upstream_send(&parent
->rpf
, parent
,
1488 static void pim_upstream_keep_alive_timer(struct thread
*t
)
1490 struct pim_upstream
*up
;
1494 /* pull the stats and re-check */
1495 if (pim_upstream_sg_running_proc(up
))
1496 /* kat was restarted because of new activity */
1499 pim_upstream_keep_alive_timer_proc(up
);
1502 void pim_upstream_keep_alive_timer_start(struct pim_upstream
*up
, uint32_t time
)
1504 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1505 if (PIM_DEBUG_PIM_TRACE
)
1506 zlog_debug("kat start on %s with no stream reference",
1509 THREAD_OFF(up
->t_ka_timer
);
1510 thread_add_timer(router
->master
, pim_upstream_keep_alive_timer
, up
,
1511 time
, &up
->t_ka_timer
);
1513 /* any time keepalive is started against a SG we will have to
1514 * re-evaluate our active source database */
1515 pim_msdp_sa_local_update(up
);
1516 /* JoinDesired can change when KAT is started or stopped */
1517 pim_upstream_update_join_desired(up
->pim
, up
);
1520 /* MSDP on RP needs to know if a source is registerable to this RP */
1521 static void pim_upstream_msdp_reg_timer(struct thread
*t
)
1523 struct pim_upstream
*up
= THREAD_ARG(t
);
1524 struct pim_instance
*pim
= up
->channel_oil
->pim
;
1526 /* source is no longer active - pull the SA from MSDP's cache */
1527 pim_msdp_sa_local_del(pim
, &up
->sg
);
1530 void pim_upstream_msdp_reg_timer_start(struct pim_upstream
*up
)
1532 THREAD_OFF(up
->t_msdp_reg_timer
);
1533 thread_add_timer(router
->master
, pim_upstream_msdp_reg_timer
, up
,
1534 PIM_MSDP_REG_RXED_PERIOD
, &up
->t_msdp_reg_timer
);
1536 pim_msdp_sa_local_update(up
);
1540 * 4.2.1 Last-Hop Switchover to the SPT
1542 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1543 * RP. Once traffic from sources to joined groups arrives at a last-hop
1544 * router, it has the option of switching to receive the traffic on a
1545 * shortest path tree (SPT).
1547 * The decision for a router to switch to the SPT is controlled as
1551 * CheckSwitchToSpt(S,G) {
1552 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1553 * (+) pim_include(S,G) != NULL )
1554 * AND SwitchToSptDesired(S,G) ) {
1555 * # Note: Restarting the KAT will result in the SPT switch
1556 * set KeepaliveTimer(S,G) to Keepalive_Period
1560 * SwitchToSptDesired(S,G) is a policy function that is implementation
1561 * defined. An "infinite threshold" policy can be implemented by making
1562 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1563 * first packet" policy can be implemented by making
1564 * SwitchToSptDesired(S,G) return true once a single packet has been
1565 * received for the source and group.
1567 int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance
*pim
,
1570 if (I_am_RP(pim
, sg
->grp
))
1576 int pim_upstream_is_sg_rpt(struct pim_upstream
*up
)
1578 struct listnode
*chnode
;
1579 struct pim_ifchannel
*ch
;
1581 for (ALL_LIST_ELEMENTS_RO(up
->ifchannels
, chnode
, ch
)) {
1582 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1589 * After receiving a packet set SPTbit:
1591 * Update_SPTbit(S,G,iif) {
1592 * if ( iif == RPF_interface(S)
1593 * AND JoinDesired(S,G) == true
1594 * AND ( DirectlyConnected(S) == true
1595 * OR RPF_interface(S) != RPF_interface(RP(G))
1596 * OR inherited_olist(S,G,rpt) == NULL
1597 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1598 * ( RPF'(S,G) != NULL ) )
1599 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1600 * Set SPTbit(S,G) to true
1604 void pim_upstream_set_sptbit(struct pim_upstream
*up
,
1605 struct interface
*incoming
)
1607 struct pim_upstream
*starup
= up
->parent
;
1609 // iif == RPF_interfvace(S)
1610 if (up
->rpf
.source_nexthop
.interface
!= incoming
) {
1611 if (PIM_DEBUG_PIM_TRACE
)
1613 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1614 __func__
, incoming
->name
,
1615 up
->rpf
.source_nexthop
.interface
->name
);
1619 // AND JoinDesired(S,G) == true
1620 if (!pim_upstream_evaluate_join_desired(up
->channel_oil
->pim
, up
)) {
1621 if (PIM_DEBUG_PIM_TRACE
)
1622 zlog_debug("%s: %s Join is not Desired", __func__
,
1627 // DirectlyConnected(S) == true
1628 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1630 if (PIM_DEBUG_PIM_TRACE
)
1631 zlog_debug("%s: %s is directly connected to the source",
1632 __func__
, up
->sg_str
);
1633 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1637 // OR RPF_interface(S) != RPF_interface(RP(G))
1639 || up
->rpf
.source_nexthop
1640 .interface
!= starup
->rpf
.source_nexthop
.interface
) {
1641 struct pim_upstream
*starup
= up
->parent
;
1643 if (PIM_DEBUG_PIM_TRACE
)
1645 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1646 __func__
, up
->sg_str
);
1647 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1649 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1653 // OR inherited_olist(S,G,rpt) == NULL
1654 if (pim_upstream_is_sg_rpt(up
)
1655 && pim_upstream_empty_inherited_olist(up
)) {
1656 if (PIM_DEBUG_PIM_TRACE
)
1657 zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
1658 __func__
, up
->sg_str
);
1659 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1663 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1664 // ( RPF'(S,G) != NULL ) )
1665 if (up
->parent
&& pim_rpf_is_same(&up
->rpf
, &up
->parent
->rpf
)) {
1666 if (PIM_DEBUG_PIM_TRACE
)
1667 zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
1668 __func__
, up
->sg_str
);
1669 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1676 const char *pim_upstream_state2str(enum pim_upstream_state join_state
)
1678 switch (join_state
) {
1679 case PIM_UPSTREAM_NOTJOINED
:
1681 case PIM_UPSTREAM_JOINED
:
1687 const char *pim_reg_state2str(enum pim_reg_state reg_state
, char *state_str
,
1688 size_t state_str_len
)
1690 switch (reg_state
) {
1691 case PIM_REG_NOINFO
:
1692 strlcpy(state_str
, "RegNoInfo", state_str_len
);
1695 strlcpy(state_str
, "RegJoined", state_str_len
);
1697 case PIM_REG_JOIN_PENDING
:
1698 strlcpy(state_str
, "RegJoinPend", state_str_len
);
1701 strlcpy(state_str
, "RegPrune", state_str_len
);
1707 static void pim_upstream_register_stop_timer(struct thread
*t
)
1709 struct pim_interface
*pim_ifp
;
1710 struct pim_instance
*pim
;
1711 struct pim_upstream
*up
;
1713 pim
= up
->channel_oil
->pim
;
1715 if (PIM_DEBUG_PIM_TRACE
) {
1716 char state_str
[PIM_REG_STATE_STR_LEN
];
1717 zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s",
1718 __func__
, up
->sg_str
, pim
->vrf
->name
,
1719 pim_reg_state2str(up
->reg_state
, state_str
,
1720 sizeof(state_str
)));
1723 switch (up
->reg_state
) {
1724 case PIM_REG_JOIN_PENDING
:
1725 up
->reg_state
= PIM_REG_JOIN
;
1726 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1727 PIM_OIF_FLAG_PROTO_PIM
,
1729 pim_vxlan_update_sg_reg_state(pim
, up
, true /*reg_join*/);
1734 /* This is equalent to Couldreg -> False */
1735 if (!up
->rpf
.source_nexthop
.interface
) {
1736 if (PIM_DEBUG_PIM_TRACE
)
1737 zlog_debug("%s: up %s RPF is not present",
1738 __func__
, up
->sg_str
);
1739 up
->reg_state
= PIM_REG_NOINFO
;
1743 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1745 if (PIM_DEBUG_PIM_TRACE
)
1747 "%s: Interface: %s is not configured for pim",
1749 up
->rpf
.source_nexthop
.interface
->name
);
1752 up
->reg_state
= PIM_REG_JOIN_PENDING
;
1753 pim_upstream_start_register_stop_timer(up
, 1);
1755 if (((up
->channel_oil
->cc
.lastused
/ 100)
1756 > pim
->keep_alive_time
)
1757 && (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
))) {
1758 if (PIM_DEBUG_PIM_TRACE
)
1760 "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
1764 pim_null_register_send(up
);
1766 case PIM_REG_NOINFO
:
1771 void pim_upstream_start_register_stop_timer(struct pim_upstream
*up
,
1776 THREAD_OFF(up
->t_rs_timer
);
1778 if (!null_register
) {
1779 uint32_t lower
= (0.5 * router
->register_suppress_time
);
1780 uint32_t upper
= (1.5 * router
->register_suppress_time
);
1781 time
= lower
+ (frr_weak_random() % (upper
- lower
+ 1));
1782 /* Make sure we don't wrap around */
1783 if (time
>= router
->register_probe_time
)
1784 time
-= router
->register_probe_time
;
1788 time
= router
->register_probe_time
;
1790 if (PIM_DEBUG_PIM_TRACE
) {
1792 "%s: (S,G)=%s Starting upstream register stop timer %d",
1793 __func__
, up
->sg_str
, time
);
1795 thread_add_timer(router
->master
, pim_upstream_register_stop_timer
, up
,
1796 time
, &up
->t_rs_timer
);
1799 int pim_upstream_inherited_olist_decide(struct pim_instance
*pim
,
1800 struct pim_upstream
*up
)
1802 struct interface
*ifp
;
1803 struct pim_ifchannel
*ch
, *starch
;
1804 struct pim_upstream
*starup
= up
->parent
;
1805 int output_intf
= 0;
1807 if (!up
->rpf
.source_nexthop
.interface
)
1808 if (PIM_DEBUG_PIM_TRACE
)
1809 zlog_debug("%s: up %s RPF is not present", __func__
,
1812 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1813 struct pim_interface
*pim_ifp
;
1817 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1820 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
1827 pim_ifp
= ifp
->info
;
1828 if (PIM_I_am_DualActive(pim_ifp
)
1829 && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
)
1830 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->flags
)
1831 || !PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
)))
1833 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1838 flag
= PIM_OIF_FLAG_PROTO_STAR
;
1840 if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch
->flags
))
1841 flag
= PIM_OIF_FLAG_PROTO_IGMP
;
1842 if (PIM_IF_FLAG_TEST_PROTO_PIM(ch
->flags
))
1843 flag
|= PIM_OIF_FLAG_PROTO_PIM
;
1845 flag
|= PIM_OIF_FLAG_PROTO_STAR
;
1848 pim_channel_add_oif(up
->channel_oil
, ifp
, flag
,
1858 * For a given upstream, determine the inherited_olist
1861 * inherited_olist(S,G,rpt) =
1862 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1863 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1864 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1866 * inherited_olist(S,G) =
1867 * inherited_olist(S,G,rpt) (+)
1868 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1870 * return 1 if there are any output interfaces
1871 * return 0 if there are not any output interfaces
1873 int pim_upstream_inherited_olist(struct pim_instance
*pim
,
1874 struct pim_upstream
*up
)
1876 int output_intf
= pim_upstream_inherited_olist_decide(pim
, up
);
1879 * If we have output_intf switch state to Join and work like normal
1880 * If we don't have an output_intf that means we are probably a
1881 * switch on a stick so turn on forwarding to just accept the
1882 * incoming packets so we don't bother the other stuff!
1884 pim_upstream_update_join_desired(pim
, up
);
1892 int pim_upstream_empty_inherited_olist(struct pim_upstream
*up
)
1894 return pim_channel_oil_empty(up
->channel_oil
);
1898 * When we have a new neighbor,
1899 * find upstreams that don't have their rpf_addr
1900 * set and see if the new neighbor allows
1901 * the join to be sent
1903 void pim_upstream_find_new_rpf(struct pim_instance
*pim
)
1905 struct pim_upstream
*up
;
1907 enum pim_rpf_result rpf_result
;
1910 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1912 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
1913 if (pim_addr_is_any(up
->upstream_addr
)) {
1914 if (PIM_DEBUG_PIM_TRACE
)
1916 "%s: RP not configured for Upstream %s",
1917 __func__
, up
->sg_str
);
1921 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
1922 if (PIM_DEBUG_PIM_TRACE
)
1924 "%s: Upstream %s without a path to send join, checking",
1925 __func__
, up
->sg_str
);
1926 old
.source_nexthop
.interface
=
1927 up
->rpf
.source_nexthop
.interface
;
1928 rpf_result
= pim_rpf_update(pim
, up
, &old
, __func__
);
1929 if (rpf_result
== PIM_RPF_CHANGED
||
1930 (rpf_result
== PIM_RPF_FAILURE
&&
1931 old
.source_nexthop
.interface
))
1932 pim_zebra_upstream_rpf_changed(pim
, up
, &old
);
1933 /* update kernel multicast forwarding cache (MFC) */
1934 pim_upstream_mroute_iif_update(up
->channel_oil
,
1938 pim_zebra_update_all_interfaces(pim
);
1941 unsigned int pim_upstream_hash_key(const void *arg
)
1943 const struct pim_upstream
*up
= arg
;
1945 return pim_sgaddr_hash(up
->sg
, 0);
1948 void pim_upstream_terminate(struct pim_instance
*pim
)
1950 struct pim_upstream
*up
;
1952 while ((up
= rb_pim_upstream_first(&pim
->upstream_head
))) {
1953 pim_upstream_del(pim
, up
, __func__
);
1954 pim_upstream_timers_stop(up
);
1957 rb_pim_upstream_fini(&pim
->upstream_head
);
1959 if (pim
->upstream_sg_wheel
)
1960 wheel_delete(pim
->upstream_sg_wheel
);
1961 pim
->upstream_sg_wheel
= NULL
;
1964 bool pim_upstream_equal(const void *arg1
, const void *arg2
)
1966 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
1967 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
1969 return !pim_sgaddr_cmp(up1
->sg
, up2
->sg
);
1972 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1973 * the cases where kat has to be restarted on rxing traffic -
1975 * if( DirectlyConnected(S) == true AND iif == RPF_interface(S) ) {
1976 * set KeepaliveTimer(S,G) to Keepalive_Period
1977 * # Note: a register state transition or UpstreamJPState(S,G)
1978 * # transition may happen as a result of restarting
1979 * # KeepaliveTimer, and must be dealt with here.
1981 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1982 * inherited_olist(S,G) != NULL ) {
1983 * set KeepaliveTimer(S,G) to Keepalive_Period
1986 static bool pim_upstream_kat_start_ok(struct pim_upstream
*up
)
1988 struct channel_oil
*c_oil
= up
->channel_oil
;
1989 struct interface
*ifp
= up
->rpf
.source_nexthop
.interface
;
1990 struct pim_interface
*pim_ifp
;
1992 /* "iif == RPF_interface(S)" check is not easy to do as the info
1993 * we get from the kernel/ASIC is really a "lookup/key hit".
1994 * So we will do an approximate check here to avoid starting KAT
1995 * because of (S,G,rpt) forwarding on a non-LHR.
2000 pim_ifp
= ifp
->info
;
2001 if (pim_ifp
->mroute_vif_index
!= *oil_parent(c_oil
))
2004 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
2009 if ((up
->join_state
== PIM_UPSTREAM_JOINED
)
2010 && !pim_upstream_empty_inherited_olist(up
)) {
2017 static bool pim_upstream_sg_running_proc(struct pim_upstream
*up
)
2020 struct pim_instance
*pim
= up
->pim
;
2022 if (!up
->channel_oil
->installed
)
2025 pim_mroute_update_counters(up
->channel_oil
);
2027 // Have we seen packets?
2028 if ((up
->channel_oil
->cc
.oldpktcnt
>= up
->channel_oil
->cc
.pktcnt
)
2029 && (up
->channel_oil
->cc
.lastused
/ 100 > 30)) {
2030 if (PIM_DEBUG_PIM_TRACE
) {
2032 "%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
2033 __func__
, up
->sg_str
, pim
->vrf
->name
,
2034 up
->channel_oil
->cc
.oldpktcnt
,
2035 up
->channel_oil
->cc
.pktcnt
,
2036 up
->channel_oil
->cc
.lastused
/ 100);
2041 if (pim_upstream_kat_start_ok(up
)) {
2042 /* Add a source reference to the stream if
2043 * one doesn't already exist */
2044 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
2045 if (PIM_DEBUG_PIM_TRACE
)
2047 "source reference created on kat restart %s[%s]",
2048 up
->sg_str
, pim
->vrf
->name
);
2050 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM
,
2052 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
2053 pim_upstream_fhr_kat_start(up
);
2055 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
2057 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
2058 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
2062 if ((up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
) &&
2063 (up
->rpf
.source_nexthop
.interface
)) {
2064 pim_upstream_set_sptbit(up
, up
->rpf
.source_nexthop
.interface
);
2071 * Code to check and see if we've received packets on a S,G mroute
2072 * and if so to set the SPT bit appropriately
2074 static void pim_upstream_sg_running(void *arg
)
2076 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
2077 struct pim_instance
*pim
= up
->channel_oil
->pim
;
2079 // No packet can have arrived here if this is the case
2080 if (!up
->channel_oil
->installed
) {
2081 if (PIM_DEBUG_TRACE
)
2082 zlog_debug("%s: %s%s is not installed in mroute",
2083 __func__
, up
->sg_str
, pim
->vrf
->name
);
2088 * This is a bit of a hack
2089 * We've noted that we should rescan but
2090 * we've missed the window for doing so in
2091 * pim_zebra.c for some reason. I am
2092 * only doing this at this point in time
2093 * to get us up and working for the moment
2095 if (up
->channel_oil
->oil_inherited_rescan
) {
2096 if (PIM_DEBUG_TRACE
)
2098 "%s: Handling unscanned inherited_olist for %s[%s]",
2099 __func__
, up
->sg_str
, pim
->vrf
->name
);
2100 pim_upstream_inherited_olist_decide(pim
, up
);
2101 up
->channel_oil
->oil_inherited_rescan
= 0;
2104 pim_upstream_sg_running_proc(up
);
2107 void pim_upstream_add_lhr_star_pimreg(struct pim_instance
*pim
)
2109 struct pim_upstream
*up
;
2111 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
2112 if (!pim_addr_is_any(up
->sg
.src
))
2115 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up
->flags
))
2118 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
2119 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2123 void pim_upstream_spt_prefix_list_update(struct pim_instance
*pim
,
2124 struct prefix_list
*pl
)
2126 const char *pname
= prefix_list_name(pl
);
2128 if (pim
->spt
.plist
&& strcmp(pim
->spt
.plist
, pname
) == 0) {
2129 pim_upstream_remove_lhr_star_pimreg(pim
, pname
);
2134 * nlist -> The new prefix list
2136 * Per Group Application of pimreg to the OIL
2137 * If the prefix list tells us DENY then
2138 * we need to Switchover to SPT immediate
2139 * so add the pimreg.
2140 * If the prefix list tells us to ACCEPT than
2141 * we need to Never do the SPT so remove
2145 void pim_upstream_remove_lhr_star_pimreg(struct pim_instance
*pim
,
2148 struct pim_upstream
*up
;
2149 struct prefix_list
*np
;
2151 enum prefix_list_type apply_new
;
2153 np
= prefix_list_lookup(PIM_AFI
, nlist
);
2155 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
2156 if (!pim_addr_is_any(up
->sg
.src
))
2159 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up
->flags
))
2163 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
2164 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2167 pim_addr_to_prefix(&g
, up
->sg
.grp
);
2168 apply_new
= prefix_list_apply(np
, &g
);
2169 if (apply_new
== PREFIX_DENY
)
2170 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
2171 PIM_OIF_FLAG_PROTO_IGMP
,
2174 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
2175 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2179 void pim_upstream_init(struct pim_instance
*pim
)
2183 snprintf(name
, sizeof(name
), "PIM %s Timer Wheel", pim
->vrf
->name
);
2184 pim
->upstream_sg_wheel
=
2185 wheel_init(router
->master
, 31000, 100, pim_upstream_hash_key
,
2186 pim_upstream_sg_running
, name
);
2188 rb_pim_upstream_init(&pim
->upstream_head
);