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 struct pim_upstream
*pim_upstream_del(struct pim_instance
*pim
,
182 struct pim_upstream
*up
, const char *name
)
184 struct listnode
*node
, *nnode
;
185 struct pim_ifchannel
*ch
;
186 bool notify_msdp
= false;
189 if (PIM_DEBUG_PIM_TRACE
)
191 "%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
192 __func__
, name
, up
->sg_str
, pim
->vrf
->name
,
193 up
->ref_count
, up
->flags
,
194 up
->channel_oil
->oil_ref_count
);
196 assert(up
->ref_count
> 0);
200 if (up
->ref_count
>= 1)
204 zlog_debug("pim_upstream free vrf:%s %s flags 0x%x",
205 pim
->vrf
->name
, up
->sg_str
, up
->flags
);
207 if (pim_up_mlag_is_local(up
))
208 pim_mlag_up_local_del(pim
, up
);
210 THREAD_OFF(up
->t_ka_timer
);
211 THREAD_OFF(up
->t_rs_timer
);
212 THREAD_OFF(up
->t_msdp_reg_timer
);
214 if (up
->join_state
== PIM_UPSTREAM_JOINED
) {
215 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0);
217 if (pim_addr_is_any(up
->sg
.src
)) {
218 /* if a (*, G) entry in the joined state is being
220 * need to notify MSDP */
226 pim_jp_agg_upstream_verification(up
, false);
227 up
->rpf
.source_nexthop
.interface
= NULL
;
229 if (!pim_addr_is_any(up
->sg
.src
)) {
230 if (pim
->upstream_sg_wheel
)
231 wheel_remove_item(pim
->upstream_sg_wheel
, up
);
235 pim_mroute_del(up
->channel_oil
, __func__
);
236 upstream_channel_oil_detach(up
);
238 for (ALL_LIST_ELEMENTS(up
->ifchannels
, node
, nnode
, ch
))
239 pim_ifchannel_delete(ch
);
240 list_delete(&up
->ifchannels
);
242 pim_upstream_remove_children(pim
, up
);
244 list_delete(&up
->sources
);
246 if (up
->parent
&& up
->parent
->sources
)
247 listnode_delete(up
->parent
->sources
, up
);
250 rb_pim_upstream_del(&pim
->upstream_head
, up
);
253 pim_msdp_up_del(pim
, &up
->sg
);
256 /* When RP gets deleted, pim_rp_del() deregister addr with Zebra NHT
257 * and assign up->upstream_addr as INADDR_ANY.
258 * So before de-registering the upstream address, check if is not equal
259 * to INADDR_ANY. This is done in order to avoid de-registering for
260 * 255.255.255.255 which is maintained for some reason..
262 if (!pim_addr_is_any(up
->upstream_addr
)) {
263 /* Deregister addr with Zebra NHT */
264 pim_addr_to_prefix(&nht_p
, up
->upstream_addr
);
265 if (PIM_DEBUG_PIM_TRACE
)
267 "%s: Deregister upstream %s addr %pFX with Zebra NHT",
268 __func__
, up
->sg_str
, &nht_p
);
269 pim_delete_tracked_nexthop(pim
, &nht_p
, up
, NULL
);
272 XFREE(MTYPE_PIM_UPSTREAM
, up
);
277 void pim_upstream_send_join(struct pim_upstream
*up
)
279 if (!up
->rpf
.source_nexthop
.interface
) {
280 if (PIM_DEBUG_PIM_TRACE
)
281 zlog_debug("%s: up %s RPF is not present", __func__
,
286 if (PIM_DEBUG_PIM_TRACE
) {
287 char rpf_str
[PREFIX_STRLEN
];
288 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
290 zlog_debug("%s: RPF'%s=%s(%s) for Interface %s", __func__
,
292 pim_upstream_state2str(up
->join_state
),
293 up
->rpf
.source_nexthop
.interface
->name
);
294 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
295 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
296 __func__
, up
->sg_str
, rpf_str
);
301 /* send Join(S,G) to the current upstream neighbor */
302 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 1 /* join */);
305 static void on_join_timer(struct thread
*t
)
307 struct pim_upstream
*up
;
311 if (!up
->rpf
.source_nexthop
.interface
) {
312 if (PIM_DEBUG_PIM_TRACE
)
313 zlog_debug("%s: up %s RPF is not present", __func__
,
319 * In the case of a HFR we will not ahve anyone to send this to.
321 if (PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
325 * Don't send the join if the outgoing interface is a loopback
326 * But since this might change leave the join timer running
328 if (up
->rpf
.source_nexthop
329 .interface
&& !if_is_loopback(up
->rpf
.source_nexthop
.interface
))
330 pim_upstream_send_join(up
);
332 join_timer_start(up
);
335 static void join_timer_stop(struct pim_upstream
*up
)
337 struct pim_neighbor
*nbr
= NULL
;
339 THREAD_OFF(up
->t_join_timer
);
341 if (up
->rpf
.source_nexthop
.interface
)
342 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
346 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
, nbr
);
348 pim_jp_agg_upstream_verification(up
, false);
351 void join_timer_start(struct pim_upstream
*up
)
353 struct pim_neighbor
*nbr
= NULL
;
355 if (up
->rpf
.source_nexthop
.interface
) {
356 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
359 if (PIM_DEBUG_PIM_EVENTS
) {
361 "%s: starting %d sec timer for upstream (S,G)=%s",
362 __func__
, router
->t_periodic
, up
->sg_str
);
367 pim_jp_agg_add_group(nbr
->upstream_jp_agg
, up
, 1, nbr
);
369 THREAD_OFF(up
->t_join_timer
);
370 thread_add_timer(router
->master
, on_join_timer
, up
,
371 router
->t_periodic
, &up
->t_join_timer
);
373 pim_jp_agg_upstream_verification(up
, true);
377 * This is only called when we are switching the upstream
378 * J/P from one neighbor to another
380 * As such we need to remove from the old list and
381 * add to the new list.
383 void pim_upstream_join_timer_restart(struct pim_upstream
*up
,
386 // THREAD_OFF(up->t_join_timer);
387 join_timer_start(up
);
390 static void pim_upstream_join_timer_restart_msec(struct pim_upstream
*up
,
393 if (PIM_DEBUG_PIM_EVENTS
) {
394 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
395 __func__
, interval_msec
, up
->sg_str
);
398 THREAD_OFF(up
->t_join_timer
);
399 thread_add_timer_msec(router
->master
, on_join_timer
, up
, interval_msec
,
403 void pim_update_suppress_timers(uint32_t suppress_time
)
405 struct pim_instance
*pim
;
407 unsigned int old_rp_ka_time
;
409 /* stash the old one so we know which values were manually configured */
410 old_rp_ka_time
= (3 * router
->register_suppress_time
411 + router
->register_probe_time
);
412 router
->register_suppress_time
= suppress_time
;
414 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
419 /* Only adjust if not manually configured */
420 if (pim
->rp_keep_alive_time
== old_rp_ka_time
)
421 pim
->rp_keep_alive_time
= PIM_RP_KEEPALIVE_PERIOD
;
425 void pim_upstream_join_suppress(struct pim_upstream
*up
, struct prefix rpf
,
428 long t_joinsuppress_msec
;
429 long join_timer_remain_msec
= 0;
430 struct pim_neighbor
*nbr
= NULL
;
432 if (!up
->rpf
.source_nexthop
.interface
) {
433 if (PIM_DEBUG_PIM_TRACE
)
434 zlog_debug("%s: up %s RPF is not present", __func__
,
439 t_joinsuppress_msec
=
440 MIN(pim_if_t_suppressed_msec(up
->rpf
.source_nexthop
.interface
),
443 if (up
->t_join_timer
)
444 join_timer_remain_msec
=
445 pim_time_timer_remain_msec(up
->t_join_timer
);
447 /* Remove it from jp agg from the nbr for suppression */
448 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
451 join_timer_remain_msec
=
452 pim_time_timer_remain_msec(nbr
->jp_timer
);
456 if (PIM_DEBUG_PIM_TRACE
) {
457 char rpf_str
[INET_ADDRSTRLEN
];
459 pim_addr_dump("<rpf?>", &rpf
, rpf_str
, sizeof(rpf_str
));
461 "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
462 __FILE__
, __func__
, up
->sg_str
, rpf_str
,
463 join_timer_remain_msec
, t_joinsuppress_msec
);
466 if (join_timer_remain_msec
< t_joinsuppress_msec
) {
467 if (PIM_DEBUG_PIM_TRACE
) {
469 "%s %s: suppressing Join(S,G)=%s for %ld msec",
470 __FILE__
, __func__
, up
->sg_str
,
471 t_joinsuppress_msec
);
475 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
, nbr
);
477 pim_upstream_join_timer_restart_msec(up
, t_joinsuppress_msec
);
481 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label
,
482 struct pim_upstream
*up
)
484 long join_timer_remain_msec
;
487 if (!up
->rpf
.source_nexthop
.interface
) {
488 if (PIM_DEBUG_PIM_TRACE
)
489 zlog_debug("%s: up %s RPF is not present", __func__
,
495 pim_if_t_override_msec(up
->rpf
.source_nexthop
.interface
);
497 if (up
->t_join_timer
) {
498 join_timer_remain_msec
=
499 pim_time_timer_remain_msec(up
->t_join_timer
);
501 /* upstream join tracked with neighbor jp timer */
502 struct pim_neighbor
*nbr
;
504 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
507 join_timer_remain_msec
=
508 pim_time_timer_remain_msec(nbr
->jp_timer
);
510 /* Manipulate such that override takes place */
511 join_timer_remain_msec
= t_override_msec
+ 1;
514 if (PIM_DEBUG_PIM_TRACE
) {
515 char rpf_str
[INET_ADDRSTRLEN
];
517 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
521 "%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
522 debug_label
, up
->sg_str
, rpf_str
,
523 join_timer_remain_msec
, t_override_msec
);
526 if (join_timer_remain_msec
> t_override_msec
) {
527 if (PIM_DEBUG_PIM_TRACE
) {
529 "%s: decreasing (S,G)=%s join timer to t_override=%d msec",
530 debug_label
, up
->sg_str
, t_override_msec
);
533 pim_upstream_join_timer_restart_msec(up
, t_override_msec
);
537 static void forward_on(struct pim_upstream
*up
)
539 struct listnode
*chnode
;
540 struct listnode
*chnextnode
;
541 struct pim_ifchannel
*ch
= NULL
;
543 /* scan (S,G) state */
544 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
545 if (pim_macro_chisin_oiflist(ch
))
546 pim_forward_start(ch
);
548 } /* scan iface channel list */
551 static void forward_off(struct pim_upstream
*up
)
553 struct listnode
*chnode
;
554 struct listnode
*chnextnode
;
555 struct pim_ifchannel
*ch
;
557 /* scan per-interface (S,G) state */
558 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
560 pim_forward_stop(ch
);
562 } /* scan iface channel list */
565 int pim_upstream_could_register(struct pim_upstream
*up
)
567 struct pim_interface
*pim_ifp
= NULL
;
569 /* FORCE_PIMREG is a generic flag to let an app like VxLAN-AA register
570 * a source on an upstream entry even if the source is not directly
571 * connected on the IIF.
573 if (PIM_UPSTREAM_FLAG_TEST_FORCE_PIMREG(up
->flags
))
576 if (up
->rpf
.source_nexthop
.interface
)
577 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
579 if (PIM_DEBUG_PIM_TRACE
)
580 zlog_debug("%s: up %s RPF is not present", __func__
,
584 if (pim_ifp
&& PIM_I_am_DR(pim_ifp
)
585 && pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
592 /* Source registration is suppressed for SSM groups. When the SSM range changes
593 * we re-revaluate register setup for existing upstream entries */
594 void pim_upstream_register_reevaluate(struct pim_instance
*pim
)
596 struct pim_upstream
*up
;
598 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
599 /* If FHR is set CouldRegister is True. Also check if the flow
600 * is actually active; if it is not kat setup will trigger
602 * registration whenever the flow becomes active. */
603 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) ||
604 !pim_upstream_is_kat_running(up
))
607 if (pim_is_grp_ssm(pim
, up
->sg
.grp
)) {
608 /* clear the register state for SSM groups */
609 if (up
->reg_state
!= PIM_REG_NOINFO
) {
610 if (PIM_DEBUG_PIM_EVENTS
)
612 "Clear register for %s as G is now SSM",
614 /* remove regiface from the OIL if it is there*/
615 pim_channel_del_oif(up
->channel_oil
,
617 PIM_OIF_FLAG_PROTO_PIM
,
619 up
->reg_state
= PIM_REG_NOINFO
;
622 /* register ASM sources with the RP */
623 if (up
->reg_state
== PIM_REG_NOINFO
) {
624 if (PIM_DEBUG_PIM_EVENTS
)
626 "Register %s as G is now ASM",
628 pim_channel_add_oif(up
->channel_oil
,
630 PIM_OIF_FLAG_PROTO_PIM
,
632 up
->reg_state
= PIM_REG_JOIN
;
638 /* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
640 * 1. along the SPT if SPTbit is set
641 * 2. and along the RPT if SPTbit is not set
642 * If forwarding is hw accelerated i.e. control and dataplane components
643 * are separate you may not be able to reliably set SPT bit on intermediate
644 * routers while still fowarding on the (S,G,rpt).
646 * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
647 * criteria to decide between using the RPT vs. SPT for forwarding.
649 void pim_upstream_update_use_rpt(struct pim_upstream
*up
,
655 if (pim_addr_is_any(up
->sg
.src
))
658 old_use_rpt
= !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
660 /* We will use the SPT (IIF=RPF_interface(S) if -
661 * 1. We have decided to join the SPT
663 * 3. Source is directly connected
664 * 4. We are RP (parent's IIF is lo or vrf-device)
665 * In all other cases the source will stay along the RPT and
666 * IIF=RPF_interface(RP).
668 if (up
->join_state
== PIM_UPSTREAM_JOINED
||
669 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) ||
670 pim_if_connected_to_source(
671 up
->rpf
.source_nexthop
.interface
,
673 /* XXX - need to switch this to a more efficient
676 I_am_RP(up
->pim
, up
->sg
.grp
))
678 PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up
->flags
);
681 PIM_UPSTREAM_FLAG_SET_USE_RPT(up
->flags
);
683 new_use_rpt
= !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
684 if (old_use_rpt
!= new_use_rpt
) {
685 if (PIM_DEBUG_PIM_EVENTS
)
686 zlog_debug("%s switched from %s to %s",
688 old_use_rpt
?"RPT":"SPT",
689 new_use_rpt
?"RPT":"SPT");
691 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
695 /* some events like RP change require re-evaluation of SGrpt across
698 void pim_upstream_reeval_use_rpt(struct pim_instance
*pim
)
700 struct pim_upstream
*up
;
702 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
703 if (pim_addr_is_any(up
->sg
.src
))
706 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
710 void pim_upstream_switch(struct pim_instance
*pim
, struct pim_upstream
*up
,
711 enum pim_upstream_state new_state
)
713 enum pim_upstream_state old_state
= up
->join_state
;
715 if (pim_addr_is_any(up
->upstream_addr
)) {
716 if (PIM_DEBUG_PIM_EVENTS
)
717 zlog_debug("%s: RPF not configured for %s", __func__
,
722 if (!up
->rpf
.source_nexthop
.interface
) {
723 if (PIM_DEBUG_PIM_EVENTS
)
724 zlog_debug("%s: RP not reachable for %s", __func__
,
729 if (PIM_DEBUG_PIM_EVENTS
) {
730 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
731 __func__
, up
->sg_str
,
732 pim_upstream_state2str(up
->join_state
),
733 pim_upstream_state2str(new_state
));
736 up
->join_state
= new_state
;
737 if (old_state
!= new_state
)
738 up
->state_transition
= pim_time_monotonic_sec();
740 pim_upstream_update_assert_tracking_desired(up
);
742 if (new_state
== PIM_UPSTREAM_JOINED
) {
743 pim_upstream_inherited_olist_decide(pim
, up
);
744 if (old_state
!= PIM_UPSTREAM_JOINED
) {
745 int old_fhr
= PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
);
747 pim_msdp_up_join_state_changed(pim
, up
);
748 if (pim_upstream_could_register(up
)) {
749 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
751 && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(
753 pim_upstream_keep_alive_timer_start(
754 up
, pim
->keep_alive_time
);
755 pim_register_join(up
);
758 pim_upstream_send_join(up
);
759 join_timer_start(up
);
762 if (old_state
!= new_state
)
763 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
767 bool send_xg_jp
= false;
771 * RFC 4601 Sec 4.5.7:
772 * JoinDesired(S,G) -> False, set SPTbit to false.
774 if (!pim_addr_is_any(up
->sg
.src
))
775 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
777 if (old_state
== PIM_UPSTREAM_JOINED
)
778 pim_msdp_up_join_state_changed(pim
, up
);
780 if (old_state
!= new_state
) {
782 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
783 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
785 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
787 (new_use_rpt
!= old_use_rpt
) &&
789 /* we have decided to switch from the SPT back
790 * to the RPT which means we need to cancel
791 * any previously sent SGrpt prunes immediately
796 /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
798 If I am RP for G then send S,G prune to its IIF. */
799 if (pim_upstream_is_sg_rpt(up
) && up
->parent
&&
800 !I_am_RP(pim
, up
->sg
.grp
))
803 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0 /* prune */);
806 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
808 "re-join RPT; *,G IIF %s S,G IIF %s ",
809 up
->parent
->rpf
.source_nexthop
.interface
?
810 up
->parent
->rpf
.source_nexthop
.interface
->name
812 up
->rpf
.source_nexthop
.interface
?
813 up
->rpf
.source_nexthop
.interface
->name
:
815 pim_jp_agg_single_upstream_send(&up
->parent
->rpf
,
823 int pim_upstream_compare(const struct pim_upstream
*up1
,
824 const struct pim_upstream
*up2
)
826 return pim_sgaddr_cmp(up1
->sg
, up2
->sg
);
829 void pim_upstream_fill_static_iif(struct pim_upstream
*up
,
830 struct interface
*incoming
)
832 up
->rpf
.source_nexthop
.interface
= incoming
;
834 /* reset other parameters to matched a connected incoming interface */
835 pim_addr_to_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
,
837 up
->rpf
.source_nexthop
.mrib_metric_preference
=
838 ZEBRA_CONNECT_DISTANCE_DEFAULT
;
839 up
->rpf
.source_nexthop
.mrib_route_metric
= 0;
840 up
->rpf
.rpf_addr
.family
= AF_INET
;
841 up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
= PIM_NET_INADDR_ANY
;
845 static struct pim_upstream
*pim_upstream_new(struct pim_instance
*pim
,
847 struct interface
*incoming
,
849 struct pim_ifchannel
*ch
)
851 enum pim_rpf_result rpf_result
;
852 struct pim_interface
*pim_ifp
;
853 struct pim_upstream
*up
;
855 up
= XCALLOC(MTYPE_PIM_UPSTREAM
, sizeof(*up
));
859 snprintfrr(up
->sg_str
, sizeof(up
->sg_str
), "%pSG", sg
);
863 rb_pim_upstream_add(&pim
->upstream_head
, up
);
864 /* Set up->upstream_addr as INADDR_ANY, if RP is not
865 * configured and retain the upstream data structure
867 if (!pim_rp_set_upstream_addr(pim
, &up
->upstream_addr
, sg
->src
,
869 if (PIM_DEBUG_PIM_TRACE
)
870 zlog_debug("%s: Received a (*,G) with no RP configured",
874 up
->parent
= pim_upstream_find_parent(pim
, up
);
875 if (pim_addr_is_any(up
->sg
.src
)) {
876 up
->sources
= list_new();
878 (int (*)(void *, void *))pim_upstream_compare
;
882 pim_upstream_find_new_children(pim
, up
);
885 up
->t_join_timer
= NULL
;
886 up
->t_ka_timer
= NULL
;
887 up
->t_rs_timer
= NULL
;
888 up
->t_msdp_reg_timer
= NULL
;
889 up
->join_state
= PIM_UPSTREAM_NOTJOINED
;
890 up
->reg_state
= PIM_REG_NOINFO
;
891 up
->state_transition
= pim_time_monotonic_sec();
892 up
->channel_oil
= pim_channel_oil_add(pim
, &up
->sg
, __func__
);
893 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
895 up
->rpf
.source_nexthop
.interface
= NULL
;
896 pim_addr_to_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
,
898 up
->rpf
.source_nexthop
.mrib_metric_preference
=
899 router
->infinite_assert_metric
.metric_preference
;
900 up
->rpf
.source_nexthop
.mrib_route_metric
=
901 router
->infinite_assert_metric
.route_metric
;
902 pim_addr_to_prefix(&up
->rpf
.rpf_addr
, PIMADDR_ANY
);
903 up
->ifchannels
= list_new();
904 up
->ifchannels
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
906 if (!pim_addr_is_any(up
->sg
.src
)) {
907 wheel_add_item(pim
->upstream_sg_wheel
, up
);
909 /* Inherit the DF role from the parent (*, G) entry for
913 && PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up
->parent
->flags
)
914 && PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->parent
->flags
)) {
915 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up
->flags
);
918 "upstream %s inherited mlag non-df flag from parent",
923 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
)
924 || PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
)) {
925 pim_upstream_fill_static_iif(up
, incoming
);
926 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
928 pim_upstream_update_use_rpt(up
,
929 false /*update_mroute*/);
930 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
932 if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
))
933 pim_upstream_keep_alive_timer_start(
934 up
, pim
->keep_alive_time
);
935 } else if (!pim_addr_is_any(up
->upstream_addr
)) {
936 pim_upstream_update_use_rpt(up
,
937 false /*update_mroute*/);
938 rpf_result
= pim_rpf_update(pim
, up
, NULL
, __func__
);
939 if (rpf_result
== PIM_RPF_FAILURE
) {
940 if (PIM_DEBUG_PIM_TRACE
)
942 "%s: Attempting to create upstream(%s), Unable to RPF for source",
943 __func__
, up
->sg_str
);
946 if (up
->rpf
.source_nexthop
.interface
) {
947 pim_upstream_mroute_iif_update(up
->channel_oil
,
952 /* send the entry to the MLAG peer */
953 /* XXX - duplicate send is possible here if pim_rpf_update
954 * successfully resolved the nexthop
956 if (pim_up_mlag_is_local(up
)
957 || PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
))
958 pim_mlag_up_local_add(pim
, up
);
960 if (PIM_DEBUG_PIM_TRACE
) {
962 "%s: Created Upstream %s upstream_addr %pPAs ref count %d increment",
963 __func__
, up
->sg_str
, &up
->upstream_addr
,
970 uint32_t pim_up_mlag_local_cost(struct pim_upstream
*up
)
972 if (!(pim_up_mlag_is_local(up
))
973 && !(up
->flags
& PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE
))
974 return router
->infinite_assert_metric
.route_metric
;
976 if ((up
->rpf
.source_nexthop
.interface
==
977 up
->pim
->vxlan
.peerlink_rif
) &&
978 (up
->rpf
.source_nexthop
.mrib_route_metric
<
979 (router
->infinite_assert_metric
.route_metric
-
980 PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC
)))
981 return up
->rpf
.source_nexthop
.mrib_route_metric
+
982 PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC
;
984 return up
->rpf
.source_nexthop
.mrib_route_metric
;
987 uint32_t pim_up_mlag_peer_cost(struct pim_upstream
*up
)
989 if (!(up
->flags
& PIM_UPSTREAM_FLAG_MASK_MLAG_PEER
))
990 return router
->infinite_assert_metric
.route_metric
;
992 return up
->mlag
.peer_mrib_metric
;
995 struct pim_upstream
*pim_upstream_find(struct pim_instance
*pim
, pim_sgaddr
*sg
)
997 struct pim_upstream lookup
;
998 struct pim_upstream
*up
= NULL
;
1001 up
= rb_pim_upstream_find(&pim
->upstream_head
, &lookup
);
1005 struct pim_upstream
*pim_upstream_find_or_add(pim_sgaddr
*sg
,
1006 struct interface
*incoming
,
1007 int flags
, const char *name
)
1009 struct pim_interface
*pim_ifp
= incoming
->info
;
1011 return (pim_upstream_add(pim_ifp
->pim
, sg
, incoming
, flags
, name
,
1015 void pim_upstream_ref(struct pim_upstream
*up
, int flags
, const char *name
)
1017 /* if a local MLAG reference is being created we need to send the mroute
1020 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up
->flags
) &&
1021 PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags
)) {
1022 PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(up
->flags
);
1023 pim_mlag_up_local_add(up
->pim
, up
);
1026 /* when we go from non-FHR to FHR we need to re-eval traffic
1029 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) &&
1030 PIM_UPSTREAM_FLAG_TEST_FHR(flags
)) {
1031 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1032 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
1035 /* re-eval joinDesired; clearing peer-msdp-sa flag can
1036 * cause JD to change
1038 if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up
->flags
) &&
1039 PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags
)) {
1040 PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up
->flags
);
1041 pim_upstream_update_join_desired(up
->pim
, up
);
1046 if (PIM_DEBUG_PIM_TRACE
)
1047 zlog_debug("%s(%s): upstream %s ref count %d increment",
1048 __func__
, name
, up
->sg_str
, up
->ref_count
);
1051 struct pim_upstream
*pim_upstream_add(struct pim_instance
*pim
, pim_sgaddr
*sg
,
1052 struct interface
*incoming
, int flags
,
1054 struct pim_ifchannel
*ch
)
1056 struct pim_upstream
*up
= NULL
;
1059 up
= pim_upstream_find(pim
, sg
);
1061 pim_upstream_ref(up
, flags
, name
);
1064 up
= pim_upstream_new(pim
, sg
, incoming
, flags
, ch
);
1067 if (PIM_DEBUG_PIM_TRACE
) {
1069 zlog_debug("%s(%s): %s, iif %pFX (%s) found: %d: ref_count: %d",
1071 up
->sg_str
, &up
->rpf
.rpf_addr
, up
->rpf
.source_nexthop
.interface
?
1072 up
->rpf
.source_nexthop
.interface
->name
: "Unknown" ,
1073 found
, up
->ref_count
);
1075 zlog_debug("%s(%s): (%pSG) failure to create", __func__
,
1083 * Passed in up must be the upstream for ch. starch is NULL if no
1085 * This function is copied over from
1086 * pim_upstream_evaluate_join_desired_interface but limited to
1087 * parent (*,G)'s includes/joins.
1089 int pim_upstream_eval_inherit_if(struct pim_upstream
*up
,
1090 struct pim_ifchannel
*ch
,
1091 struct pim_ifchannel
*starch
)
1093 /* if there is an explicit prune for this interface we cannot
1097 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1101 /* Check if the OIF can be inherited fron the (*,G) entry
1104 if (!pim_macro_ch_lost_assert(starch
)
1105 && pim_macro_chisin_joins_or_include(starch
))
1113 * Passed in up must be the upstream for ch. starch is NULL if no
1116 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream
*up
,
1117 struct pim_ifchannel
*ch
,
1118 struct pim_ifchannel
*starch
)
1121 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1124 if (!pim_macro_ch_lost_assert(ch
)
1125 && pim_macro_chisin_joins_or_include(ch
))
1133 /* XXX: check on this with donald
1134 * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in
1138 if (PIM_IF_FLAG_TEST_S_G_RPT(starch
->upstream
->flags
))
1142 if (!pim_macro_ch_lost_assert(starch
)
1143 && pim_macro_chisin_joins_or_include(starch
))
1150 /* Returns true if immediate OIL is empty and is used to evaluate
1151 * JoinDesired. See pim_upstream_evaluate_join_desired.
1153 static bool pim_upstream_empty_immediate_olist(struct pim_instance
*pim
,
1154 struct pim_upstream
*up
)
1156 struct interface
*ifp
;
1157 struct pim_ifchannel
*ch
;
1159 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1163 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1167 /* If we have even one immediate OIF we can return with
1170 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1173 } /* scan iface channel list */
1175 /* immediate_oil is empty */
1180 static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream
*up
)
1182 return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up
->flags
);
1186 * bool JoinDesired(*,G) {
1187 * if (immediate_olist(*,G) != NULL)
1193 * bool JoinDesired(S,G) {
1194 * return( immediate_olist(S,G) != NULL
1195 * OR ( KeepaliveTimer(S,G) is running
1196 * AND inherited_olist(S,G) != NULL ) )
1199 bool pim_upstream_evaluate_join_desired(struct pim_instance
*pim
,
1200 struct pim_upstream
*up
)
1205 empty_imm_oil
= pim_upstream_empty_immediate_olist(pim
, up
);
1208 if (pim_addr_is_any(up
->sg
.src
))
1209 return !empty_imm_oil
;
1214 empty_inh_oil
= pim_upstream_empty_inherited_olist(up
);
1215 if (!empty_inh_oil
&&
1216 (pim_upstream_is_kat_running(up
) ||
1217 pim_upstream_is_msdp_peer_sa(up
)))
1224 See also pim_upstream_evaluate_join_desired() above.
1226 void pim_upstream_update_join_desired(struct pim_instance
*pim
,
1227 struct pim_upstream
*up
)
1229 int was_join_desired
; /* boolean */
1230 int is_join_desired
; /* boolean */
1232 was_join_desired
= PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up
->flags
);
1234 is_join_desired
= pim_upstream_evaluate_join_desired(pim
, up
);
1235 if (is_join_desired
)
1236 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up
->flags
);
1238 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up
->flags
);
1240 /* switched from false to true */
1241 if (is_join_desired
&& (up
->join_state
== PIM_UPSTREAM_NOTJOINED
)) {
1242 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_JOINED
);
1246 /* switched from true to false */
1247 if (!is_join_desired
&& was_join_desired
) {
1248 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
1254 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
1255 Transitions from Joined State
1256 RPF'(S,G) GenID changes
1258 The upstream (S,G) state machine remains in Joined state. If the
1259 Join Timer is set to expire in more than t_override seconds, reset
1260 it so that it expires after t_override seconds.
1262 void pim_upstream_rpf_genid_changed(struct pim_instance
*pim
,
1263 pim_addr neigh_addr
)
1265 struct pim_upstream
*up
;
1268 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1270 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
1273 rpf_addr
= pim_addr_from_prefix(&up
->rpf
.rpf_addr
);
1275 if (PIM_DEBUG_PIM_TRACE
)
1277 "%s: matching neigh=%pPA against upstream (S,G)=%s[%s] joined=%d rpf_addr=%pPA",
1278 __func__
, &neigh_addr
, up
->sg_str
,
1280 up
->join_state
== PIM_UPSTREAM_JOINED
,
1283 /* consider only (S,G) upstream in Joined state */
1284 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
1287 /* match RPF'(S,G)=neigh_addr */
1288 if (pim_addr_cmp(rpf_addr
, neigh_addr
))
1291 pim_upstream_join_timer_decrease_to_t_override(
1292 "RPF'(S,G) GenID change", up
);
1297 void pim_upstream_rpf_interface_changed(struct pim_upstream
*up
,
1298 struct interface
*old_rpf_ifp
)
1300 struct listnode
*chnode
;
1301 struct listnode
*chnextnode
;
1302 struct pim_ifchannel
*ch
;
1304 /* search all ifchannels */
1305 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1306 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1308 /* RPF_interface(S) was NOT I */
1309 (old_rpf_ifp
== ch
->interface
) &&
1310 /* RPF_interface(S) stopped being I */
1311 (ch
->upstream
->rpf
.source_nexthop
1313 (ch
->upstream
->rpf
.source_nexthop
1314 .interface
!= ch
->interface
)) {
1315 assert_action_a5(ch
);
1317 } /* PIM_IFASSERT_I_AM_LOSER */
1319 pim_ifchannel_update_assert_tracking_desired(ch
);
1323 void pim_upstream_update_could_assert(struct pim_upstream
*up
)
1325 struct listnode
*chnode
;
1326 struct listnode
*chnextnode
;
1327 struct pim_ifchannel
*ch
;
1329 /* scan per-interface (S,G) state */
1330 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1331 pim_ifchannel_update_could_assert(ch
);
1332 } /* scan iface channel list */
1335 void pim_upstream_update_my_assert_metric(struct pim_upstream
*up
)
1337 struct listnode
*chnode
;
1338 struct listnode
*chnextnode
;
1339 struct pim_ifchannel
*ch
;
1341 /* scan per-interface (S,G) state */
1342 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1343 pim_ifchannel_update_my_assert_metric(ch
);
1345 } /* scan iface channel list */
1348 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
)
1350 struct listnode
*chnode
;
1351 struct listnode
*chnextnode
;
1352 struct pim_interface
*pim_ifp
;
1353 struct pim_ifchannel
*ch
;
1355 /* scan per-interface (S,G) state */
1356 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1359 pim_ifp
= ch
->interface
->info
;
1363 pim_ifchannel_update_assert_tracking_desired(ch
);
1365 } /* scan iface channel list */
1368 /* When kat is stopped CouldRegister goes to false so we need to
1369 * transition the (S, G) on FHR to NI state and remove reg tunnel
1371 static void pim_upstream_fhr_kat_expiry(struct pim_instance
*pim
,
1372 struct pim_upstream
*up
)
1374 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
1377 if (PIM_DEBUG_PIM_TRACE
)
1378 zlog_debug("kat expired on %s; clear fhr reg state",
1381 /* stop reg-stop timer */
1382 THREAD_OFF(up
->t_rs_timer
);
1383 /* remove regiface from the OIL if it is there*/
1384 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
1385 PIM_OIF_FLAG_PROTO_PIM
, __func__
);
1386 /* clear the register state */
1387 up
->reg_state
= PIM_REG_NOINFO
;
1388 PIM_UPSTREAM_FLAG_UNSET_FHR(up
->flags
);
1391 /* When kat is started CouldRegister can go to true. And if it does we
1392 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1394 static void pim_upstream_fhr_kat_start(struct pim_upstream
*up
)
1396 if (pim_upstream_could_register(up
)) {
1397 if (PIM_DEBUG_PIM_TRACE
)
1399 "kat started on %s; set fhr reg state to joined",
1402 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1403 if (up
->reg_state
== PIM_REG_NOINFO
)
1404 pim_register_join(up
);
1405 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
1410 * On an RP, the PMBR value must be cleared when the
1411 * Keepalive Timer expires
1412 * KAT expiry indicates that flow is inactive. If the flow was created or
1413 * maintained by activity now is the time to deref it.
1415 struct pim_upstream
*pim_upstream_keep_alive_timer_proc(
1416 struct pim_upstream
*up
)
1418 struct pim_instance
*pim
;
1420 pim
= up
->channel_oil
->pim
;
1422 if (PIM_UPSTREAM_FLAG_TEST_DISABLE_KAT_EXPIRY(up
->flags
)) {
1423 /* if the router is a PIM vxlan encapsulator we prevent expiry
1424 * of KAT as the mroute is pre-setup without any traffic
1426 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
1430 if (I_am_RP(pim
, up
->sg
.grp
)) {
1431 pim_br_clear_pmbr(&up
->sg
);
1433 * We need to do more here :)
1434 * But this is the start.
1438 /* source is no longer active - pull the SA from MSDP's cache */
1439 pim_msdp_sa_local_del(pim
, &up
->sg
);
1441 /* JoinDesired can change when KAT is started or stopped */
1442 pim_upstream_update_join_desired(pim
, up
);
1444 /* if entry was created because of activity we need to deref it */
1445 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1446 pim_upstream_fhr_kat_expiry(pim
, up
);
1447 if (PIM_DEBUG_PIM_TRACE
)
1449 "kat expired on %s[%s]; remove stream reference",
1450 up
->sg_str
, pim
->vrf
->name
);
1451 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up
->flags
);
1453 /* Return if upstream entry got deleted.*/
1454 if (!pim_upstream_del(pim
, up
, __func__
))
1457 if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
)) {
1458 PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(up
->flags
);
1460 if (!pim_upstream_del(pim
, up
, __func__
))
1464 /* upstream reference would have been added to track the local
1465 * membership if it is LHR. We have to clear it when KAT expires.
1466 * Otherwise would result in stale entry with uncleared ref count.
1468 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
1469 struct pim_upstream
*parent
= up
->parent
;
1471 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up
->flags
);
1472 up
= pim_upstream_del(pim
, up
, __func__
);
1475 pim_jp_agg_single_upstream_send(&parent
->rpf
, parent
,
1482 static void pim_upstream_keep_alive_timer(struct thread
*t
)
1484 struct pim_upstream
*up
;
1488 /* pull the stats and re-check */
1489 if (pim_upstream_sg_running_proc(up
))
1490 /* kat was restarted because of new activity */
1493 pim_upstream_keep_alive_timer_proc(up
);
1496 void pim_upstream_keep_alive_timer_start(struct pim_upstream
*up
, uint32_t time
)
1498 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1499 if (PIM_DEBUG_PIM_TRACE
)
1500 zlog_debug("kat start on %s with no stream reference",
1503 THREAD_OFF(up
->t_ka_timer
);
1504 thread_add_timer(router
->master
, pim_upstream_keep_alive_timer
, up
,
1505 time
, &up
->t_ka_timer
);
1507 /* any time keepalive is started against a SG we will have to
1508 * re-evaluate our active source database */
1509 pim_msdp_sa_local_update(up
);
1510 /* JoinDesired can change when KAT is started or stopped */
1511 pim_upstream_update_join_desired(up
->pim
, up
);
1514 /* MSDP on RP needs to know if a source is registerable to this RP */
1515 static void pim_upstream_msdp_reg_timer(struct thread
*t
)
1517 struct pim_upstream
*up
= THREAD_ARG(t
);
1518 struct pim_instance
*pim
= up
->channel_oil
->pim
;
1520 /* source is no longer active - pull the SA from MSDP's cache */
1521 pim_msdp_sa_local_del(pim
, &up
->sg
);
1524 void pim_upstream_msdp_reg_timer_start(struct pim_upstream
*up
)
1526 THREAD_OFF(up
->t_msdp_reg_timer
);
1527 thread_add_timer(router
->master
, pim_upstream_msdp_reg_timer
, up
,
1528 PIM_MSDP_REG_RXED_PERIOD
, &up
->t_msdp_reg_timer
);
1530 pim_msdp_sa_local_update(up
);
1534 * 4.2.1 Last-Hop Switchover to the SPT
1536 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1537 * RP. Once traffic from sources to joined groups arrives at a last-hop
1538 * router, it has the option of switching to receive the traffic on a
1539 * shortest path tree (SPT).
1541 * The decision for a router to switch to the SPT is controlled as
1545 * CheckSwitchToSpt(S,G) {
1546 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1547 * (+) pim_include(S,G) != NULL )
1548 * AND SwitchToSptDesired(S,G) ) {
1549 * # Note: Restarting the KAT will result in the SPT switch
1550 * set KeepaliveTimer(S,G) to Keepalive_Period
1554 * SwitchToSptDesired(S,G) is a policy function that is implementation
1555 * defined. An "infinite threshold" policy can be implemented by making
1556 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1557 * first packet" policy can be implemented by making
1558 * SwitchToSptDesired(S,G) return true once a single packet has been
1559 * received for the source and group.
1561 int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance
*pim
,
1564 if (I_am_RP(pim
, sg
->grp
))
1570 int pim_upstream_is_sg_rpt(struct pim_upstream
*up
)
1572 struct listnode
*chnode
;
1573 struct pim_ifchannel
*ch
;
1575 for (ALL_LIST_ELEMENTS_RO(up
->ifchannels
, chnode
, ch
)) {
1576 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1583 * After receiving a packet set SPTbit:
1585 * Update_SPTbit(S,G,iif) {
1586 * if ( iif == RPF_interface(S)
1587 * AND JoinDesired(S,G) == true
1588 * AND ( DirectlyConnected(S) == true
1589 * OR RPF_interface(S) != RPF_interface(RP(G))
1590 * OR inherited_olist(S,G,rpt) == NULL
1591 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1592 * ( RPF'(S,G) != NULL ) )
1593 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1594 * Set SPTbit(S,G) to true
1598 void pim_upstream_set_sptbit(struct pim_upstream
*up
,
1599 struct interface
*incoming
)
1601 struct pim_upstream
*starup
= up
->parent
;
1603 // iif == RPF_interfvace(S)
1604 if (up
->rpf
.source_nexthop
.interface
!= incoming
) {
1605 if (PIM_DEBUG_PIM_TRACE
)
1607 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1608 __func__
, incoming
->name
,
1609 up
->rpf
.source_nexthop
.interface
->name
);
1613 // AND JoinDesired(S,G) == true
1614 if (!pim_upstream_evaluate_join_desired(up
->channel_oil
->pim
, up
)) {
1615 if (PIM_DEBUG_PIM_TRACE
)
1616 zlog_debug("%s: %s Join is not Desired", __func__
,
1621 // DirectlyConnected(S) == true
1622 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1624 if (PIM_DEBUG_PIM_TRACE
)
1625 zlog_debug("%s: %s is directly connected to the source",
1626 __func__
, up
->sg_str
);
1627 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1631 // OR RPF_interface(S) != RPF_interface(RP(G))
1633 || up
->rpf
.source_nexthop
1634 .interface
!= starup
->rpf
.source_nexthop
.interface
) {
1635 struct pim_upstream
*starup
= up
->parent
;
1637 if (PIM_DEBUG_PIM_TRACE
)
1639 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1640 __func__
, up
->sg_str
);
1641 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1643 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1647 // OR inherited_olist(S,G,rpt) == NULL
1648 if (pim_upstream_is_sg_rpt(up
)
1649 && pim_upstream_empty_inherited_olist(up
)) {
1650 if (PIM_DEBUG_PIM_TRACE
)
1651 zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
1652 __func__
, up
->sg_str
);
1653 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1657 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1658 // ( RPF'(S,G) != NULL ) )
1659 if (up
->parent
&& pim_rpf_is_same(&up
->rpf
, &up
->parent
->rpf
)) {
1660 if (PIM_DEBUG_PIM_TRACE
)
1661 zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
1662 __func__
, up
->sg_str
);
1663 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1670 const char *pim_upstream_state2str(enum pim_upstream_state join_state
)
1672 switch (join_state
) {
1673 case PIM_UPSTREAM_NOTJOINED
:
1675 case PIM_UPSTREAM_JOINED
:
1681 const char *pim_reg_state2str(enum pim_reg_state reg_state
, char *state_str
,
1682 size_t state_str_len
)
1684 switch (reg_state
) {
1685 case PIM_REG_NOINFO
:
1686 strlcpy(state_str
, "RegNoInfo", state_str_len
);
1689 strlcpy(state_str
, "RegJoined", state_str_len
);
1691 case PIM_REG_JOIN_PENDING
:
1692 strlcpy(state_str
, "RegJoinPend", state_str_len
);
1695 strlcpy(state_str
, "RegPrune", state_str_len
);
1701 static void pim_upstream_register_stop_timer(struct thread
*t
)
1703 struct pim_interface
*pim_ifp
;
1704 struct pim_instance
*pim
;
1705 struct pim_upstream
*up
;
1707 pim
= up
->channel_oil
->pim
;
1709 if (PIM_DEBUG_PIM_TRACE
) {
1710 char state_str
[PIM_REG_STATE_STR_LEN
];
1711 zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s",
1712 __func__
, up
->sg_str
, pim
->vrf
->name
,
1713 pim_reg_state2str(up
->reg_state
, state_str
,
1714 sizeof(state_str
)));
1717 switch (up
->reg_state
) {
1718 case PIM_REG_JOIN_PENDING
:
1719 up
->reg_state
= PIM_REG_JOIN
;
1720 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1721 PIM_OIF_FLAG_PROTO_PIM
,
1723 pim_vxlan_update_sg_reg_state(pim
, up
, true /*reg_join*/);
1728 /* This is equalent to Couldreg -> False */
1729 if (!up
->rpf
.source_nexthop
.interface
) {
1730 if (PIM_DEBUG_PIM_TRACE
)
1731 zlog_debug("%s: up %s RPF is not present",
1732 __func__
, up
->sg_str
);
1733 up
->reg_state
= PIM_REG_NOINFO
;
1737 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1739 if (PIM_DEBUG_PIM_TRACE
)
1741 "%s: Interface: %s is not configured for pim",
1743 up
->rpf
.source_nexthop
.interface
->name
);
1746 up
->reg_state
= PIM_REG_JOIN_PENDING
;
1747 pim_upstream_start_register_stop_timer(up
, 1);
1749 if (((up
->channel_oil
->cc
.lastused
/ 100)
1750 > pim
->keep_alive_time
)
1751 && (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
))) {
1752 if (PIM_DEBUG_PIM_TRACE
)
1754 "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
1758 pim_null_register_send(up
);
1760 case PIM_REG_NOINFO
:
1765 void pim_upstream_start_register_stop_timer(struct pim_upstream
*up
,
1770 THREAD_OFF(up
->t_rs_timer
);
1772 if (!null_register
) {
1773 uint32_t lower
= (0.5 * router
->register_suppress_time
);
1774 uint32_t upper
= (1.5 * router
->register_suppress_time
);
1775 time
= lower
+ (frr_weak_random() % (upper
- lower
+ 1));
1776 /* Make sure we don't wrap around */
1777 if (time
>= router
->register_probe_time
)
1778 time
-= router
->register_probe_time
;
1782 time
= router
->register_probe_time
;
1784 if (PIM_DEBUG_PIM_TRACE
) {
1786 "%s: (S,G)=%s Starting upstream register stop timer %d",
1787 __func__
, up
->sg_str
, time
);
1789 thread_add_timer(router
->master
, pim_upstream_register_stop_timer
, up
,
1790 time
, &up
->t_rs_timer
);
1793 int pim_upstream_inherited_olist_decide(struct pim_instance
*pim
,
1794 struct pim_upstream
*up
)
1796 struct interface
*ifp
;
1797 struct pim_ifchannel
*ch
, *starch
;
1798 struct pim_upstream
*starup
= up
->parent
;
1799 int output_intf
= 0;
1801 if (!up
->rpf
.source_nexthop
.interface
)
1802 if (PIM_DEBUG_PIM_TRACE
)
1803 zlog_debug("%s: up %s RPF is not present", __func__
,
1806 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1807 struct pim_interface
*pim_ifp
;
1811 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1814 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
1821 pim_ifp
= ifp
->info
;
1822 if (PIM_I_am_DualActive(pim_ifp
)
1823 && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
)
1824 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->flags
)
1825 || !PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
)))
1827 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1832 flag
= PIM_OIF_FLAG_PROTO_STAR
;
1834 if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch
->flags
))
1835 flag
= PIM_OIF_FLAG_PROTO_IGMP
;
1836 if (PIM_IF_FLAG_TEST_PROTO_PIM(ch
->flags
))
1837 flag
|= PIM_OIF_FLAG_PROTO_PIM
;
1839 flag
|= PIM_OIF_FLAG_PROTO_STAR
;
1842 pim_channel_add_oif(up
->channel_oil
, ifp
, flag
,
1852 * For a given upstream, determine the inherited_olist
1855 * inherited_olist(S,G,rpt) =
1856 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1857 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1858 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1860 * inherited_olist(S,G) =
1861 * inherited_olist(S,G,rpt) (+)
1862 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1864 * return 1 if there are any output interfaces
1865 * return 0 if there are not any output interfaces
1867 int pim_upstream_inherited_olist(struct pim_instance
*pim
,
1868 struct pim_upstream
*up
)
1870 int output_intf
= pim_upstream_inherited_olist_decide(pim
, up
);
1873 * If we have output_intf switch state to Join and work like normal
1874 * If we don't have an output_intf that means we are probably a
1875 * switch on a stick so turn on forwarding to just accept the
1876 * incoming packets so we don't bother the other stuff!
1878 pim_upstream_update_join_desired(pim
, up
);
1886 int pim_upstream_empty_inherited_olist(struct pim_upstream
*up
)
1888 return pim_channel_oil_empty(up
->channel_oil
);
1892 * When we have a new neighbor,
1893 * find upstreams that don't have their rpf_addr
1894 * set and see if the new neighbor allows
1895 * the join to be sent
1897 void pim_upstream_find_new_rpf(struct pim_instance
*pim
)
1899 struct pim_upstream
*up
;
1901 enum pim_rpf_result rpf_result
;
1904 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1906 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
1907 if (pim_addr_is_any(up
->upstream_addr
)) {
1908 if (PIM_DEBUG_PIM_TRACE
)
1910 "%s: RP not configured for Upstream %s",
1911 __func__
, up
->sg_str
);
1915 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
1916 if (PIM_DEBUG_PIM_TRACE
)
1918 "%s: Upstream %s without a path to send join, checking",
1919 __func__
, up
->sg_str
);
1920 old
.source_nexthop
.interface
=
1921 up
->rpf
.source_nexthop
.interface
;
1922 rpf_result
= pim_rpf_update(pim
, up
, &old
, __func__
);
1923 if (rpf_result
== PIM_RPF_CHANGED
||
1924 (rpf_result
== PIM_RPF_FAILURE
&&
1925 old
.source_nexthop
.interface
))
1926 pim_zebra_upstream_rpf_changed(pim
, up
, &old
);
1927 /* update kernel multicast forwarding cache (MFC) */
1928 pim_upstream_mroute_iif_update(up
->channel_oil
,
1932 pim_zebra_update_all_interfaces(pim
);
1935 unsigned int pim_upstream_hash_key(const void *arg
)
1937 const struct pim_upstream
*up
= arg
;
1939 return pim_sgaddr_hash(up
->sg
, 0);
1942 void pim_upstream_terminate(struct pim_instance
*pim
)
1944 struct pim_upstream
*up
;
1946 while ((up
= rb_pim_upstream_first(&pim
->upstream_head
))) {
1947 pim_upstream_del(pim
, up
, __func__
);
1950 rb_pim_upstream_fini(&pim
->upstream_head
);
1952 if (pim
->upstream_sg_wheel
)
1953 wheel_delete(pim
->upstream_sg_wheel
);
1954 pim
->upstream_sg_wheel
= NULL
;
1957 bool pim_upstream_equal(const void *arg1
, const void *arg2
)
1959 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
1960 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
1962 return !pim_sgaddr_cmp(up1
->sg
, up2
->sg
);
1965 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1966 * the cases where kat has to be restarted on rxing traffic -
1968 * if( DirectlyConnected(S) == true AND iif == RPF_interface(S) ) {
1969 * set KeepaliveTimer(S,G) to Keepalive_Period
1970 * # Note: a register state transition or UpstreamJPState(S,G)
1971 * # transition may happen as a result of restarting
1972 * # KeepaliveTimer, and must be dealt with here.
1974 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1975 * inherited_olist(S,G) != NULL ) {
1976 * set KeepaliveTimer(S,G) to Keepalive_Period
1979 static bool pim_upstream_kat_start_ok(struct pim_upstream
*up
)
1981 struct channel_oil
*c_oil
= up
->channel_oil
;
1982 struct interface
*ifp
= up
->rpf
.source_nexthop
.interface
;
1983 struct pim_interface
*pim_ifp
;
1985 /* "iif == RPF_interface(S)" check is not easy to do as the info
1986 * we get from the kernel/ASIC is really a "lookup/key hit".
1987 * So we will do an approximate check here to avoid starting KAT
1988 * because of (S,G,rpt) forwarding on a non-LHR.
1993 pim_ifp
= ifp
->info
;
1994 if (pim_ifp
->mroute_vif_index
!= *oil_parent(c_oil
))
1997 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
2002 if ((up
->join_state
== PIM_UPSTREAM_JOINED
)
2003 && !pim_upstream_empty_inherited_olist(up
)) {
2010 static bool pim_upstream_sg_running_proc(struct pim_upstream
*up
)
2013 struct pim_instance
*pim
= up
->pim
;
2015 if (!up
->channel_oil
->installed
)
2018 pim_mroute_update_counters(up
->channel_oil
);
2020 // Have we seen packets?
2021 if ((up
->channel_oil
->cc
.oldpktcnt
>= up
->channel_oil
->cc
.pktcnt
)
2022 && (up
->channel_oil
->cc
.lastused
/ 100 > 30)) {
2023 if (PIM_DEBUG_PIM_TRACE
) {
2025 "%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
2026 __func__
, up
->sg_str
, pim
->vrf
->name
,
2027 up
->channel_oil
->cc
.oldpktcnt
,
2028 up
->channel_oil
->cc
.pktcnt
,
2029 up
->channel_oil
->cc
.lastused
/ 100);
2034 if (pim_upstream_kat_start_ok(up
)) {
2035 /* Add a source reference to the stream if
2036 * one doesn't already exist */
2037 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
2038 if (PIM_DEBUG_PIM_TRACE
)
2040 "source reference created on kat restart %s[%s]",
2041 up
->sg_str
, pim
->vrf
->name
);
2043 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM
,
2045 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
2046 pim_upstream_fhr_kat_start(up
);
2048 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
2050 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
2051 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
2055 if ((up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
) &&
2056 (up
->rpf
.source_nexthop
.interface
)) {
2057 pim_upstream_set_sptbit(up
, up
->rpf
.source_nexthop
.interface
);
2064 * Code to check and see if we've received packets on a S,G mroute
2065 * and if so to set the SPT bit appropriately
2067 static void pim_upstream_sg_running(void *arg
)
2069 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
2070 struct pim_instance
*pim
= up
->channel_oil
->pim
;
2072 // No packet can have arrived here if this is the case
2073 if (!up
->channel_oil
->installed
) {
2074 if (PIM_DEBUG_TRACE
)
2075 zlog_debug("%s: %s%s is not installed in mroute",
2076 __func__
, up
->sg_str
, pim
->vrf
->name
);
2081 * This is a bit of a hack
2082 * We've noted that we should rescan but
2083 * we've missed the window for doing so in
2084 * pim_zebra.c for some reason. I am
2085 * only doing this at this point in time
2086 * to get us up and working for the moment
2088 if (up
->channel_oil
->oil_inherited_rescan
) {
2089 if (PIM_DEBUG_TRACE
)
2091 "%s: Handling unscanned inherited_olist for %s[%s]",
2092 __func__
, up
->sg_str
, pim
->vrf
->name
);
2093 pim_upstream_inherited_olist_decide(pim
, up
);
2094 up
->channel_oil
->oil_inherited_rescan
= 0;
2097 pim_upstream_sg_running_proc(up
);
2100 void pim_upstream_add_lhr_star_pimreg(struct pim_instance
*pim
)
2102 struct pim_upstream
*up
;
2104 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
2105 if (!pim_addr_is_any(up
->sg
.src
))
2108 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up
->flags
))
2111 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
2112 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2116 void pim_upstream_spt_prefix_list_update(struct pim_instance
*pim
,
2117 struct prefix_list
*pl
)
2119 const char *pname
= prefix_list_name(pl
);
2121 if (pim
->spt
.plist
&& strcmp(pim
->spt
.plist
, pname
) == 0) {
2122 pim_upstream_remove_lhr_star_pimreg(pim
, pname
);
2127 * nlist -> The new prefix list
2129 * Per Group Application of pimreg to the OIL
2130 * If the prefix list tells us DENY then
2131 * we need to Switchover to SPT immediate
2132 * so add the pimreg.
2133 * If the prefix list tells us to ACCEPT than
2134 * we need to Never do the SPT so remove
2138 void pim_upstream_remove_lhr_star_pimreg(struct pim_instance
*pim
,
2141 struct pim_upstream
*up
;
2142 struct prefix_list
*np
;
2144 enum prefix_list_type apply_new
;
2146 np
= prefix_list_lookup(PIM_AFI
, nlist
);
2148 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
2149 if (!pim_addr_is_any(up
->sg
.src
))
2152 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up
->flags
))
2156 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
2157 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2160 pim_addr_to_prefix(&g
, up
->sg
.grp
);
2161 apply_new
= prefix_list_apply(np
, &g
);
2162 if (apply_new
== PREFIX_DENY
)
2163 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
2164 PIM_OIF_FLAG_PROTO_IGMP
,
2167 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
2168 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2172 void pim_upstream_init(struct pim_instance
*pim
)
2176 snprintf(name
, sizeof(name
), "PIM %s Timer Wheel", pim
->vrf
->name
);
2177 pim
->upstream_sg_wheel
=
2178 wheel_init(router
->master
, 31000, 100, pim_upstream_hash_key
,
2179 pim_upstream_sg_running
, name
);
2181 rb_pim_upstream_init(&pim
->upstream_head
);