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 int 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
);
337 static void join_timer_stop(struct pim_upstream
*up
)
339 struct pim_neighbor
*nbr
= NULL
;
341 THREAD_OFF(up
->t_join_timer
);
343 if (up
->rpf
.source_nexthop
.interface
)
344 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
348 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
, nbr
);
350 pim_jp_agg_upstream_verification(up
, false);
353 void join_timer_start(struct pim_upstream
*up
)
355 struct pim_neighbor
*nbr
= NULL
;
357 if (up
->rpf
.source_nexthop
.interface
) {
358 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
361 if (PIM_DEBUG_PIM_EVENTS
) {
363 "%s: starting %d sec timer for upstream (S,G)=%s",
364 __func__
, router
->t_periodic
, up
->sg_str
);
369 pim_jp_agg_add_group(nbr
->upstream_jp_agg
, up
, 1, nbr
);
371 THREAD_OFF(up
->t_join_timer
);
372 thread_add_timer(router
->master
, on_join_timer
, up
,
373 router
->t_periodic
, &up
->t_join_timer
);
375 pim_jp_agg_upstream_verification(up
, true);
379 * This is only called when we are switching the upstream
380 * J/P from one neighbor to another
382 * As such we need to remove from the old list and
383 * add to the new list.
385 void pim_upstream_join_timer_restart(struct pim_upstream
*up
,
388 // THREAD_OFF(up->t_join_timer);
389 join_timer_start(up
);
392 static void pim_upstream_join_timer_restart_msec(struct pim_upstream
*up
,
395 if (PIM_DEBUG_PIM_EVENTS
) {
396 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
397 __func__
, interval_msec
, up
->sg_str
);
400 THREAD_OFF(up
->t_join_timer
);
401 thread_add_timer_msec(router
->master
, on_join_timer
, up
, interval_msec
,
405 void pim_update_suppress_timers(uint32_t suppress_time
)
407 struct pim_instance
*pim
;
409 unsigned int old_rp_ka_time
;
411 /* stash the old one so we know which values were manually configured */
412 old_rp_ka_time
= (3 * router
->register_suppress_time
413 + router
->register_probe_time
);
414 router
->register_suppress_time
= suppress_time
;
416 RB_FOREACH (vrf
, vrf_name_head
, &vrfs_by_name
) {
421 /* Only adjust if not manually configured */
422 if (pim
->rp_keep_alive_time
== old_rp_ka_time
)
423 pim
->rp_keep_alive_time
= PIM_RP_KEEPALIVE_PERIOD
;
427 void pim_upstream_join_suppress(struct pim_upstream
*up
,
428 struct in_addr rpf_addr
, int holdtime
)
430 long t_joinsuppress_msec
;
431 long join_timer_remain_msec
= 0;
432 struct pim_neighbor
*nbr
= NULL
;
434 if (!up
->rpf
.source_nexthop
.interface
) {
435 if (PIM_DEBUG_PIM_TRACE
)
436 zlog_debug("%s: up %s RPF is not present", __func__
,
441 t_joinsuppress_msec
=
442 MIN(pim_if_t_suppressed_msec(up
->rpf
.source_nexthop
.interface
),
445 if (up
->t_join_timer
)
446 join_timer_remain_msec
=
447 pim_time_timer_remain_msec(up
->t_join_timer
);
449 /* Remove it from jp agg from the nbr for suppression */
450 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
453 join_timer_remain_msec
=
454 pim_time_timer_remain_msec(nbr
->jp_timer
);
458 if (PIM_DEBUG_PIM_TRACE
) {
459 char rpf_str
[INET_ADDRSTRLEN
];
460 pim_inet4_dump("<rpf?>", rpf_addr
, rpf_str
, sizeof(rpf_str
));
462 "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
463 __FILE__
, __func__
, up
->sg_str
, rpf_str
,
464 join_timer_remain_msec
, t_joinsuppress_msec
);
467 if (join_timer_remain_msec
< t_joinsuppress_msec
) {
468 if (PIM_DEBUG_PIM_TRACE
) {
470 "%s %s: suppressing Join(S,G)=%s for %ld msec",
471 __FILE__
, __func__
, up
->sg_str
,
472 t_joinsuppress_msec
);
476 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
, nbr
);
478 pim_upstream_join_timer_restart_msec(up
, t_joinsuppress_msec
);
482 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label
,
483 struct pim_upstream
*up
)
485 long join_timer_remain_msec
;
488 if (!up
->rpf
.source_nexthop
.interface
) {
489 if (PIM_DEBUG_PIM_TRACE
)
490 zlog_debug("%s: up %s RPF is not present", __func__
,
496 pim_if_t_override_msec(up
->rpf
.source_nexthop
.interface
);
498 if (up
->t_join_timer
) {
499 join_timer_remain_msec
=
500 pim_time_timer_remain_msec(up
->t_join_timer
);
502 /* upstream join tracked with neighbor jp timer */
503 struct pim_neighbor
*nbr
;
505 nbr
= pim_neighbor_find_prefix(up
->rpf
.source_nexthop
.interface
,
508 join_timer_remain_msec
=
509 pim_time_timer_remain_msec(nbr
->jp_timer
);
511 /* Manipulate such that override takes place */
512 join_timer_remain_msec
= t_override_msec
+ 1;
515 if (PIM_DEBUG_PIM_TRACE
) {
516 char rpf_str
[INET_ADDRSTRLEN
];
517 pim_inet4_dump("<rpf?>", up
->rpf
.rpf_addr
.u
.prefix4
, rpf_str
,
520 "%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
521 debug_label
, up
->sg_str
, rpf_str
,
522 join_timer_remain_msec
, t_override_msec
);
525 if (join_timer_remain_msec
> t_override_msec
) {
526 if (PIM_DEBUG_PIM_TRACE
) {
528 "%s: decreasing (S,G)=%s join timer to t_override=%d msec",
529 debug_label
, up
->sg_str
, t_override_msec
);
532 pim_upstream_join_timer_restart_msec(up
, t_override_msec
);
536 static void forward_on(struct pim_upstream
*up
)
538 struct listnode
*chnode
;
539 struct listnode
*chnextnode
;
540 struct pim_ifchannel
*ch
= NULL
;
542 /* scan (S,G) state */
543 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
544 if (pim_macro_chisin_oiflist(ch
))
545 pim_forward_start(ch
);
547 } /* scan iface channel list */
550 static void forward_off(struct pim_upstream
*up
)
552 struct listnode
*chnode
;
553 struct listnode
*chnextnode
;
554 struct pim_ifchannel
*ch
;
556 /* scan per-interface (S,G) state */
557 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
559 pim_forward_stop(ch
);
561 } /* scan iface channel list */
564 int pim_upstream_could_register(struct pim_upstream
*up
)
566 struct pim_interface
*pim_ifp
= NULL
;
568 /* FORCE_PIMREG is a generic flag to let an app like VxLAN-AA register
569 * a source on an upstream entry even if the source is not directly
570 * connected on the IIF.
572 if (PIM_UPSTREAM_FLAG_TEST_FORCE_PIMREG(up
->flags
))
575 if (up
->rpf
.source_nexthop
.interface
)
576 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
578 if (PIM_DEBUG_PIM_TRACE
)
579 zlog_debug("%s: up %s RPF is not present", __func__
,
583 if (pim_ifp
&& PIM_I_am_DR(pim_ifp
)
584 && pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
591 /* Source registration is suppressed for SSM groups. When the SSM range changes
592 * we re-revaluate register setup for existing upstream entries */
593 void pim_upstream_register_reevaluate(struct pim_instance
*pim
)
595 struct pim_upstream
*up
;
597 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
598 /* If FHR is set CouldRegister is True. Also check if the flow
599 * is actually active; if it is not kat setup will trigger
601 * registration whenever the flow becomes active. */
602 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) ||
603 !pim_upstream_is_kat_running(up
))
606 if (pim_is_grp_ssm(pim
, up
->sg
.grp
)) {
607 /* clear the register state for SSM groups */
608 if (up
->reg_state
!= PIM_REG_NOINFO
) {
609 if (PIM_DEBUG_PIM_EVENTS
)
611 "Clear register for %s as G is now SSM",
613 /* remove regiface from the OIL if it is there*/
614 pim_channel_del_oif(up
->channel_oil
,
616 PIM_OIF_FLAG_PROTO_PIM
,
618 up
->reg_state
= PIM_REG_NOINFO
;
621 /* register ASM sources with the RP */
622 if (up
->reg_state
== PIM_REG_NOINFO
) {
623 if (PIM_DEBUG_PIM_EVENTS
)
625 "Register %s as G is now ASM",
627 pim_channel_add_oif(up
->channel_oil
,
629 PIM_OIF_FLAG_PROTO_PIM
,
631 up
->reg_state
= PIM_REG_JOIN
;
637 /* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
639 * 1. along the SPT if SPTbit is set
640 * 2. and along the RPT if SPTbit is not set
641 * If forwarding is hw accelerated i.e. control and dataplane components
642 * are separate you may not be able to reliably set SPT bit on intermediate
643 * routers while still fowarding on the (S,G,rpt).
645 * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
646 * criteria to decide between using the RPT vs. SPT for forwarding.
648 void pim_upstream_update_use_rpt(struct pim_upstream
*up
,
654 if (pim_addr_is_any(up
->sg
.src
))
657 old_use_rpt
= !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
659 /* We will use the SPT (IIF=RPF_interface(S) if -
660 * 1. We have decided to join the SPT
662 * 3. Source is directly connected
663 * 4. We are RP (parent's IIF is lo or vrf-device)
664 * In all other cases the source will stay along the RPT and
665 * IIF=RPF_interface(RP).
667 if (up
->join_state
== PIM_UPSTREAM_JOINED
||
668 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) ||
669 pim_if_connected_to_source(
670 up
->rpf
.source_nexthop
.interface
,
672 /* XXX - need to switch this to a more efficient
675 I_am_RP(up
->pim
, up
->sg
.grp
))
677 PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up
->flags
);
680 PIM_UPSTREAM_FLAG_SET_USE_RPT(up
->flags
);
682 new_use_rpt
= !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
683 if (old_use_rpt
!= new_use_rpt
) {
684 if (PIM_DEBUG_PIM_EVENTS
)
685 zlog_debug("%s switched from %s to %s",
687 old_use_rpt
?"RPT":"SPT",
688 new_use_rpt
?"RPT":"SPT");
690 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
694 /* some events like RP change require re-evaluation of SGrpt across
697 void pim_upstream_reeval_use_rpt(struct pim_instance
*pim
)
699 struct pim_upstream
*up
;
701 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
702 if (pim_addr_is_any(up
->sg
.src
))
705 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
709 void pim_upstream_switch(struct pim_instance
*pim
, struct pim_upstream
*up
,
710 enum pim_upstream_state new_state
)
712 enum pim_upstream_state old_state
= up
->join_state
;
714 if (pim_addr_is_any(up
->upstream_addr
)) {
715 if (PIM_DEBUG_PIM_EVENTS
)
716 zlog_debug("%s: RPF not configured for %s", __func__
,
721 if (!up
->rpf
.source_nexthop
.interface
) {
722 if (PIM_DEBUG_PIM_EVENTS
)
723 zlog_debug("%s: RP not reachable for %s", __func__
,
728 if (PIM_DEBUG_PIM_EVENTS
) {
729 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
730 __func__
, up
->sg_str
,
731 pim_upstream_state2str(up
->join_state
),
732 pim_upstream_state2str(new_state
));
735 up
->join_state
= new_state
;
736 if (old_state
!= new_state
)
737 up
->state_transition
= pim_time_monotonic_sec();
739 pim_upstream_update_assert_tracking_desired(up
);
741 if (new_state
== PIM_UPSTREAM_JOINED
) {
742 pim_upstream_inherited_olist_decide(pim
, up
);
743 if (old_state
!= PIM_UPSTREAM_JOINED
) {
744 int old_fhr
= PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
);
746 pim_msdp_up_join_state_changed(pim
, up
);
747 if (pim_upstream_could_register(up
)) {
748 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
750 && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(
752 pim_upstream_keep_alive_timer_start(
753 up
, pim
->keep_alive_time
);
754 pim_register_join(up
);
757 pim_upstream_send_join(up
);
758 join_timer_start(up
);
761 if (old_state
!= new_state
)
762 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
766 bool send_xg_jp
= false;
770 * RFC 4601 Sec 4.5.7:
771 * JoinDesired(S,G) -> False, set SPTbit to false.
773 if (!pim_addr_is_any(up
->sg
.src
))
774 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
776 if (old_state
== PIM_UPSTREAM_JOINED
)
777 pim_msdp_up_join_state_changed(pim
, up
);
779 if (old_state
!= new_state
) {
781 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
782 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
784 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up
->flags
);
786 (new_use_rpt
!= old_use_rpt
) &&
788 /* we have decided to switch from the SPT back
789 * to the RPT which means we need to cancel
790 * any previously sent SGrpt prunes immediately
795 /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
797 If I am RP for G then send S,G prune to its IIF. */
798 if (pim_upstream_is_sg_rpt(up
) && up
->parent
&&
799 !I_am_RP(pim
, up
->sg
.grp
))
802 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0 /* prune */);
805 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
807 "re-join RPT; *,G IIF %s S,G IIF %s ",
808 up
->parent
->rpf
.source_nexthop
.interface
?
809 up
->parent
->rpf
.source_nexthop
.interface
->name
811 up
->rpf
.source_nexthop
.interface
?
812 up
->rpf
.source_nexthop
.interface
->name
:
814 pim_jp_agg_single_upstream_send(&up
->parent
->rpf
,
822 int pim_upstream_compare(const struct pim_upstream
*up1
,
823 const struct pim_upstream
*up2
)
825 return pim_sgaddr_cmp(up1
->sg
, up2
->sg
);
828 void pim_upstream_fill_static_iif(struct pim_upstream
*up
,
829 struct interface
*incoming
)
831 up
->rpf
.source_nexthop
.interface
= incoming
;
833 /* reset other parameters to matched a connected incoming interface */
834 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.family
= AF_INET
;
835 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
.s_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 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.family
= AF_INET
;
897 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
.s_addr
=
899 up
->rpf
.source_nexthop
.mrib_metric_preference
=
900 router
->infinite_assert_metric
.metric_preference
;
901 up
->rpf
.source_nexthop
.mrib_route_metric
=
902 router
->infinite_assert_metric
.route_metric
;
903 up
->rpf
.rpf_addr
.family
= AF_INET
;
904 up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
= PIM_NET_INADDR_ANY
;
906 up
->ifchannels
= list_new();
907 up
->ifchannels
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
909 if (!pim_addr_is_any(up
->sg
.src
)) {
910 wheel_add_item(pim
->upstream_sg_wheel
, up
);
912 /* Inherit the DF role from the parent (*, G) entry for
916 && PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up
->parent
->flags
)
917 && PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->parent
->flags
)) {
918 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up
->flags
);
921 "upstream %s inherited mlag non-df flag from parent",
926 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
)
927 || PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
)) {
928 pim_upstream_fill_static_iif(up
, incoming
);
929 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
931 pim_upstream_update_use_rpt(up
,
932 false /*update_mroute*/);
933 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
935 if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
))
936 pim_upstream_keep_alive_timer_start(
937 up
, pim
->keep_alive_time
);
938 } else if (!pim_addr_is_any(up
->upstream_addr
)) {
939 pim_upstream_update_use_rpt(up
,
940 false /*update_mroute*/);
941 rpf_result
= pim_rpf_update(pim
, up
, NULL
, __func__
);
942 if (rpf_result
== PIM_RPF_FAILURE
) {
943 if (PIM_DEBUG_PIM_TRACE
)
945 "%s: Attempting to create upstream(%s), Unable to RPF for source",
946 __func__
, up
->sg_str
);
949 if (up
->rpf
.source_nexthop
.interface
) {
950 pim_upstream_mroute_iif_update(up
->channel_oil
,
955 /* send the entry to the MLAG peer */
956 /* XXX - duplicate send is possible here if pim_rpf_update
957 * successfully resolved the nexthop
959 if (pim_up_mlag_is_local(up
)
960 || PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
))
961 pim_mlag_up_local_add(pim
, up
);
963 if (PIM_DEBUG_PIM_TRACE
) {
965 "%s: Created Upstream %s upstream_addr %pI4 ref count %d increment",
966 __func__
, up
->sg_str
, &up
->upstream_addr
,
973 uint32_t pim_up_mlag_local_cost(struct pim_upstream
*up
)
975 if (!(pim_up_mlag_is_local(up
))
976 && !(up
->flags
& PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE
))
977 return router
->infinite_assert_metric
.route_metric
;
979 if ((up
->rpf
.source_nexthop
.interface
==
980 up
->pim
->vxlan
.peerlink_rif
) &&
981 (up
->rpf
.source_nexthop
.mrib_route_metric
<
982 (router
->infinite_assert_metric
.route_metric
-
983 PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC
)))
984 return up
->rpf
.source_nexthop
.mrib_route_metric
+
985 PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC
;
987 return up
->rpf
.source_nexthop
.mrib_route_metric
;
990 uint32_t pim_up_mlag_peer_cost(struct pim_upstream
*up
)
992 if (!(up
->flags
& PIM_UPSTREAM_FLAG_MASK_MLAG_PEER
))
993 return router
->infinite_assert_metric
.route_metric
;
995 return up
->mlag
.peer_mrib_metric
;
998 struct pim_upstream
*pim_upstream_find(struct pim_instance
*pim
, pim_sgaddr
*sg
)
1000 struct pim_upstream lookup
;
1001 struct pim_upstream
*up
= NULL
;
1004 up
= rb_pim_upstream_find(&pim
->upstream_head
, &lookup
);
1008 struct pim_upstream
*pim_upstream_find_or_add(pim_sgaddr
*sg
,
1009 struct interface
*incoming
,
1010 int flags
, const char *name
)
1012 struct pim_interface
*pim_ifp
= incoming
->info
;
1014 return (pim_upstream_add(pim_ifp
->pim
, sg
, incoming
, flags
, name
,
1018 void pim_upstream_ref(struct pim_upstream
*up
, int flags
, const char *name
)
1020 /* if a local MLAG reference is being created we need to send the mroute
1023 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up
->flags
) &&
1024 PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags
)) {
1025 PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(up
->flags
);
1026 pim_mlag_up_local_add(up
->pim
, up
);
1029 /* when we go from non-FHR to FHR we need to re-eval traffic
1032 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) &&
1033 PIM_UPSTREAM_FLAG_TEST_FHR(flags
)) {
1034 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1035 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
1038 /* re-eval joinDesired; clearing peer-msdp-sa flag can
1039 * cause JD to change
1041 if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up
->flags
) &&
1042 PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags
)) {
1043 PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up
->flags
);
1044 pim_upstream_update_join_desired(up
->pim
, up
);
1049 if (PIM_DEBUG_PIM_TRACE
)
1050 zlog_debug("%s(%s): upstream %s ref count %d increment",
1051 __func__
, name
, up
->sg_str
, up
->ref_count
);
1054 struct pim_upstream
*pim_upstream_add(struct pim_instance
*pim
, pim_sgaddr
*sg
,
1055 struct interface
*incoming
, int flags
,
1057 struct pim_ifchannel
*ch
)
1059 struct pim_upstream
*up
= NULL
;
1062 up
= pim_upstream_find(pim
, sg
);
1064 pim_upstream_ref(up
, flags
, name
);
1067 up
= pim_upstream_new(pim
, sg
, incoming
, flags
, ch
);
1070 if (PIM_DEBUG_PIM_TRACE
) {
1072 zlog_debug("%s(%s): %s, iif %pFX (%s) found: %d: ref_count: %d",
1074 up
->sg_str
, &up
->rpf
.rpf_addr
, up
->rpf
.source_nexthop
.interface
?
1075 up
->rpf
.source_nexthop
.interface
->name
: "Unknown" ,
1076 found
, up
->ref_count
);
1078 zlog_debug("%s(%s): (%pSG) failure to create", __func__
,
1086 * Passed in up must be the upstream for ch. starch is NULL if no
1088 * This function is copied over from
1089 * pim_upstream_evaluate_join_desired_interface but limited to
1090 * parent (*,G)'s includes/joins.
1092 int pim_upstream_eval_inherit_if(struct pim_upstream
*up
,
1093 struct pim_ifchannel
*ch
,
1094 struct pim_ifchannel
*starch
)
1096 /* if there is an explicit prune for this interface we cannot
1100 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1104 /* Check if the OIF can be inherited fron the (*,G) entry
1107 if (!pim_macro_ch_lost_assert(starch
)
1108 && pim_macro_chisin_joins_or_include(starch
))
1116 * Passed in up must be the upstream for ch. starch is NULL if no
1119 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream
*up
,
1120 struct pim_ifchannel
*ch
,
1121 struct pim_ifchannel
*starch
)
1124 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1127 if (!pim_macro_ch_lost_assert(ch
)
1128 && pim_macro_chisin_joins_or_include(ch
))
1136 /* XXX: check on this with donald
1137 * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in
1141 if (PIM_IF_FLAG_TEST_S_G_RPT(starch
->upstream
->flags
))
1145 if (!pim_macro_ch_lost_assert(starch
)
1146 && pim_macro_chisin_joins_or_include(starch
))
1153 /* Returns true if immediate OIL is empty and is used to evaluate
1154 * JoinDesired. See pim_upstream_evaluate_join_desired.
1156 static bool pim_upstream_empty_immediate_olist(struct pim_instance
*pim
,
1157 struct pim_upstream
*up
)
1159 struct interface
*ifp
;
1160 struct pim_ifchannel
*ch
;
1162 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1166 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1170 /* If we have even one immediate OIF we can return with
1173 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1176 } /* scan iface channel list */
1178 /* immediate_oil is empty */
1183 static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream
*up
)
1185 return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up
->flags
);
1189 * bool JoinDesired(*,G) {
1190 * if (immediate_olist(*,G) != NULL)
1196 * bool JoinDesired(S,G) {
1197 * return( immediate_olist(S,G) != NULL
1198 * OR ( KeepaliveTimer(S,G) is running
1199 * AND inherited_olist(S,G) != NULL ) )
1202 bool pim_upstream_evaluate_join_desired(struct pim_instance
*pim
,
1203 struct pim_upstream
*up
)
1208 empty_imm_oil
= pim_upstream_empty_immediate_olist(pim
, up
);
1211 if (pim_addr_is_any(up
->sg
.src
))
1212 return !empty_imm_oil
;
1217 empty_inh_oil
= pim_upstream_empty_inherited_olist(up
);
1218 if (!empty_inh_oil
&&
1219 (pim_upstream_is_kat_running(up
) ||
1220 pim_upstream_is_msdp_peer_sa(up
)))
1227 See also pim_upstream_evaluate_join_desired() above.
1229 void pim_upstream_update_join_desired(struct pim_instance
*pim
,
1230 struct pim_upstream
*up
)
1232 int was_join_desired
; /* boolean */
1233 int is_join_desired
; /* boolean */
1235 was_join_desired
= PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up
->flags
);
1237 is_join_desired
= pim_upstream_evaluate_join_desired(pim
, up
);
1238 if (is_join_desired
)
1239 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up
->flags
);
1241 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up
->flags
);
1243 /* switched from false to true */
1244 if (is_join_desired
&& (up
->join_state
== PIM_UPSTREAM_NOTJOINED
)) {
1245 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_JOINED
);
1249 /* switched from true to false */
1250 if (!is_join_desired
&& was_join_desired
) {
1251 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
1257 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
1258 Transitions from Joined State
1259 RPF'(S,G) GenID changes
1261 The upstream (S,G) state machine remains in Joined state. If the
1262 Join Timer is set to expire in more than t_override seconds, reset
1263 it so that it expires after t_override seconds.
1265 void pim_upstream_rpf_genid_changed(struct pim_instance
*pim
,
1266 struct in_addr neigh_addr
)
1268 struct pim_upstream
*up
;
1271 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1273 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
1274 if (PIM_DEBUG_PIM_TRACE
) {
1275 char rpf_addr_str
[PREFIX_STRLEN
];
1276 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_addr_str
,
1277 sizeof(rpf_addr_str
));
1279 "%s: matching neigh=%pI4 against upstream (S,G)=%s[%s] joined=%d rpf_addr=%s",
1280 __func__
, &neigh_addr
, up
->sg_str
,
1282 up
->join_state
== PIM_UPSTREAM_JOINED
,
1286 /* consider only (S,G) upstream in Joined state */
1287 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
1290 /* match RPF'(S,G)=neigh_addr */
1291 if (up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
!= neigh_addr
.s_addr
)
1294 pim_upstream_join_timer_decrease_to_t_override(
1295 "RPF'(S,G) GenID change", up
);
1300 void pim_upstream_rpf_interface_changed(struct pim_upstream
*up
,
1301 struct interface
*old_rpf_ifp
)
1303 struct listnode
*chnode
;
1304 struct listnode
*chnextnode
;
1305 struct pim_ifchannel
*ch
;
1307 /* search all ifchannels */
1308 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1309 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1311 /* RPF_interface(S) was NOT I */
1312 (old_rpf_ifp
== ch
->interface
) &&
1313 /* RPF_interface(S) stopped being I */
1314 (ch
->upstream
->rpf
.source_nexthop
1316 (ch
->upstream
->rpf
.source_nexthop
1317 .interface
!= ch
->interface
)) {
1318 assert_action_a5(ch
);
1320 } /* PIM_IFASSERT_I_AM_LOSER */
1322 pim_ifchannel_update_assert_tracking_desired(ch
);
1326 void pim_upstream_update_could_assert(struct pim_upstream
*up
)
1328 struct listnode
*chnode
;
1329 struct listnode
*chnextnode
;
1330 struct pim_ifchannel
*ch
;
1332 /* scan per-interface (S,G) state */
1333 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1334 pim_ifchannel_update_could_assert(ch
);
1335 } /* scan iface channel list */
1338 void pim_upstream_update_my_assert_metric(struct pim_upstream
*up
)
1340 struct listnode
*chnode
;
1341 struct listnode
*chnextnode
;
1342 struct pim_ifchannel
*ch
;
1344 /* scan per-interface (S,G) state */
1345 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1346 pim_ifchannel_update_my_assert_metric(ch
);
1348 } /* scan iface channel list */
1351 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
)
1353 struct listnode
*chnode
;
1354 struct listnode
*chnextnode
;
1355 struct pim_interface
*pim_ifp
;
1356 struct pim_ifchannel
*ch
;
1358 /* scan per-interface (S,G) state */
1359 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1362 pim_ifp
= ch
->interface
->info
;
1366 pim_ifchannel_update_assert_tracking_desired(ch
);
1368 } /* scan iface channel list */
1371 /* When kat is stopped CouldRegister goes to false so we need to
1372 * transition the (S, G) on FHR to NI state and remove reg tunnel
1374 static void pim_upstream_fhr_kat_expiry(struct pim_instance
*pim
,
1375 struct pim_upstream
*up
)
1377 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
1380 if (PIM_DEBUG_PIM_TRACE
)
1381 zlog_debug("kat expired on %s; clear fhr reg state",
1384 /* stop reg-stop timer */
1385 THREAD_OFF(up
->t_rs_timer
);
1386 /* remove regiface from the OIL if it is there*/
1387 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
1388 PIM_OIF_FLAG_PROTO_PIM
, __func__
);
1389 /* clear the register state */
1390 up
->reg_state
= PIM_REG_NOINFO
;
1391 PIM_UPSTREAM_FLAG_UNSET_FHR(up
->flags
);
1394 /* When kat is started CouldRegister can go to true. And if it does we
1395 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1397 static void pim_upstream_fhr_kat_start(struct pim_upstream
*up
)
1399 if (pim_upstream_could_register(up
)) {
1400 if (PIM_DEBUG_PIM_TRACE
)
1402 "kat started on %s; set fhr reg state to joined",
1405 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1406 if (up
->reg_state
== PIM_REG_NOINFO
)
1407 pim_register_join(up
);
1408 pim_upstream_update_use_rpt(up
, true /*update_mroute*/);
1413 * On an RP, the PMBR value must be cleared when the
1414 * Keepalive Timer expires
1415 * KAT expiry indicates that flow is inactive. If the flow was created or
1416 * maintained by activity now is the time to deref it.
1418 struct pim_upstream
*pim_upstream_keep_alive_timer_proc(
1419 struct pim_upstream
*up
)
1421 struct pim_instance
*pim
;
1423 pim
= up
->channel_oil
->pim
;
1425 if (PIM_UPSTREAM_FLAG_TEST_DISABLE_KAT_EXPIRY(up
->flags
)) {
1426 /* if the router is a PIM vxlan encapsulator we prevent expiry
1427 * of KAT as the mroute is pre-setup without any traffic
1429 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
1433 if (I_am_RP(pim
, up
->sg
.grp
)) {
1434 pim_br_clear_pmbr(&up
->sg
);
1436 * We need to do more here :)
1437 * But this is the start.
1441 /* source is no longer active - pull the SA from MSDP's cache */
1442 pim_msdp_sa_local_del(pim
, &up
->sg
);
1444 /* JoinDesired can change when KAT is started or stopped */
1445 pim_upstream_update_join_desired(pim
, up
);
1447 /* if entry was created because of activity we need to deref it */
1448 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1449 pim_upstream_fhr_kat_expiry(pim
, up
);
1450 if (PIM_DEBUG_PIM_TRACE
)
1452 "kat expired on %s[%s]; remove stream reference",
1453 up
->sg_str
, pim
->vrf
->name
);
1454 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up
->flags
);
1456 /* Return if upstream entry got deleted.*/
1457 if (!pim_upstream_del(pim
, up
, __func__
))
1460 if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up
->flags
)) {
1461 PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(up
->flags
);
1463 if (!pim_upstream_del(pim
, up
, __func__
))
1467 /* upstream reference would have been added to track the local
1468 * membership if it is LHR. We have to clear it when KAT expires.
1469 * Otherwise would result in stale entry with uncleared ref count.
1471 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
1472 struct pim_upstream
*parent
= up
->parent
;
1474 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up
->flags
);
1475 up
= pim_upstream_del(pim
, up
, __func__
);
1478 pim_jp_agg_single_upstream_send(&parent
->rpf
, parent
,
1485 static int pim_upstream_keep_alive_timer(struct thread
*t
)
1487 struct pim_upstream
*up
;
1491 /* pull the stats and re-check */
1492 if (pim_upstream_sg_running_proc(up
))
1493 /* kat was restarted because of new activity */
1496 pim_upstream_keep_alive_timer_proc(up
);
1500 void pim_upstream_keep_alive_timer_start(struct pim_upstream
*up
, uint32_t time
)
1502 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1503 if (PIM_DEBUG_PIM_TRACE
)
1504 zlog_debug("kat start on %s with no stream reference",
1507 THREAD_OFF(up
->t_ka_timer
);
1508 thread_add_timer(router
->master
, pim_upstream_keep_alive_timer
, up
,
1509 time
, &up
->t_ka_timer
);
1511 /* any time keepalive is started against a SG we will have to
1512 * re-evaluate our active source database */
1513 pim_msdp_sa_local_update(up
);
1514 /* JoinDesired can change when KAT is started or stopped */
1515 pim_upstream_update_join_desired(up
->pim
, up
);
1518 /* MSDP on RP needs to know if a source is registerable to this RP */
1519 static int pim_upstream_msdp_reg_timer(struct thread
*t
)
1521 struct pim_upstream
*up
= THREAD_ARG(t
);
1522 struct pim_instance
*pim
= up
->channel_oil
->pim
;
1524 /* source is no longer active - pull the SA from MSDP's cache */
1525 pim_msdp_sa_local_del(pim
, &up
->sg
);
1528 void pim_upstream_msdp_reg_timer_start(struct pim_upstream
*up
)
1530 THREAD_OFF(up
->t_msdp_reg_timer
);
1531 thread_add_timer(router
->master
, pim_upstream_msdp_reg_timer
, up
,
1532 PIM_MSDP_REG_RXED_PERIOD
, &up
->t_msdp_reg_timer
);
1534 pim_msdp_sa_local_update(up
);
1538 * 4.2.1 Last-Hop Switchover to the SPT
1540 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1541 * RP. Once traffic from sources to joined groups arrives at a last-hop
1542 * router, it has the option of switching to receive the traffic on a
1543 * shortest path tree (SPT).
1545 * The decision for a router to switch to the SPT is controlled as
1549 * CheckSwitchToSpt(S,G) {
1550 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1551 * (+) pim_include(S,G) != NULL )
1552 * AND SwitchToSptDesired(S,G) ) {
1553 * # Note: Restarting the KAT will result in the SPT switch
1554 * set KeepaliveTimer(S,G) to Keepalive_Period
1558 * SwitchToSptDesired(S,G) is a policy function that is implementation
1559 * defined. An "infinite threshold" policy can be implemented by making
1560 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1561 * first packet" policy can be implemented by making
1562 * SwitchToSptDesired(S,G) return true once a single packet has been
1563 * received for the source and group.
1565 int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance
*pim
,
1568 if (I_am_RP(pim
, sg
->grp
))
1574 int pim_upstream_is_sg_rpt(struct pim_upstream
*up
)
1576 struct listnode
*chnode
;
1577 struct pim_ifchannel
*ch
;
1579 for (ALL_LIST_ELEMENTS_RO(up
->ifchannels
, chnode
, ch
)) {
1580 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1587 * After receiving a packet set SPTbit:
1589 * Update_SPTbit(S,G,iif) {
1590 * if ( iif == RPF_interface(S)
1591 * AND JoinDesired(S,G) == true
1592 * AND ( DirectlyConnected(S) == true
1593 * OR RPF_interface(S) != RPF_interface(RP(G))
1594 * OR inherited_olist(S,G,rpt) == NULL
1595 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1596 * ( RPF'(S,G) != NULL ) )
1597 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1598 * Set SPTbit(S,G) to true
1602 void pim_upstream_set_sptbit(struct pim_upstream
*up
,
1603 struct interface
*incoming
)
1605 struct pim_upstream
*starup
= up
->parent
;
1607 // iif == RPF_interfvace(S)
1608 if (up
->rpf
.source_nexthop
.interface
!= incoming
) {
1609 if (PIM_DEBUG_PIM_TRACE
)
1611 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1612 __func__
, incoming
->name
,
1613 up
->rpf
.source_nexthop
.interface
->name
);
1617 // AND JoinDesired(S,G) == true
1618 if (!pim_upstream_evaluate_join_desired(up
->channel_oil
->pim
, up
)) {
1619 if (PIM_DEBUG_PIM_TRACE
)
1620 zlog_debug("%s: %s Join is not Desired", __func__
,
1625 // DirectlyConnected(S) == true
1626 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1628 if (PIM_DEBUG_PIM_TRACE
)
1629 zlog_debug("%s: %s is directly connected to the source",
1630 __func__
, up
->sg_str
);
1631 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1635 // OR RPF_interface(S) != RPF_interface(RP(G))
1637 || up
->rpf
.source_nexthop
1638 .interface
!= starup
->rpf
.source_nexthop
.interface
) {
1639 struct pim_upstream
*starup
= up
->parent
;
1641 if (PIM_DEBUG_PIM_TRACE
)
1643 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1644 __func__
, up
->sg_str
);
1645 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1647 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1651 // OR inherited_olist(S,G,rpt) == NULL
1652 if (pim_upstream_is_sg_rpt(up
)
1653 && pim_upstream_empty_inherited_olist(up
)) {
1654 if (PIM_DEBUG_PIM_TRACE
)
1655 zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
1656 __func__
, up
->sg_str
);
1657 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1661 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1662 // ( RPF'(S,G) != NULL ) )
1663 if (up
->parent
&& pim_rpf_is_same(&up
->rpf
, &up
->parent
->rpf
)) {
1664 if (PIM_DEBUG_PIM_TRACE
)
1665 zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
1666 __func__
, up
->sg_str
);
1667 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1674 const char *pim_upstream_state2str(enum pim_upstream_state join_state
)
1676 switch (join_state
) {
1677 case PIM_UPSTREAM_NOTJOINED
:
1679 case PIM_UPSTREAM_JOINED
:
1685 const char *pim_reg_state2str(enum pim_reg_state reg_state
, char *state_str
,
1686 size_t state_str_len
)
1688 switch (reg_state
) {
1689 case PIM_REG_NOINFO
:
1690 strlcpy(state_str
, "RegNoInfo", state_str_len
);
1693 strlcpy(state_str
, "RegJoined", state_str_len
);
1695 case PIM_REG_JOIN_PENDING
:
1696 strlcpy(state_str
, "RegJoinPend", state_str_len
);
1699 strlcpy(state_str
, "RegPrune", state_str_len
);
1705 static int pim_upstream_register_stop_timer(struct thread
*t
)
1707 struct pim_interface
*pim_ifp
;
1708 struct pim_instance
*pim
;
1709 struct pim_upstream
*up
;
1711 pim
= up
->channel_oil
->pim
;
1713 if (PIM_DEBUG_PIM_TRACE
) {
1714 char state_str
[PIM_REG_STATE_STR_LEN
];
1715 zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s",
1716 __func__
, up
->sg_str
, pim
->vrf
->name
,
1717 pim_reg_state2str(up
->reg_state
, state_str
,
1718 sizeof(state_str
)));
1721 switch (up
->reg_state
) {
1722 case PIM_REG_JOIN_PENDING
:
1723 up
->reg_state
= PIM_REG_JOIN
;
1724 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1725 PIM_OIF_FLAG_PROTO_PIM
,
1727 pim_vxlan_update_sg_reg_state(pim
, up
, true /*reg_join*/);
1732 /* This is equalent to Couldreg -> False */
1733 if (!up
->rpf
.source_nexthop
.interface
) {
1734 if (PIM_DEBUG_PIM_TRACE
)
1735 zlog_debug("%s: up %s RPF is not present",
1736 __func__
, up
->sg_str
);
1737 up
->reg_state
= PIM_REG_NOINFO
;
1741 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1743 if (PIM_DEBUG_PIM_TRACE
)
1745 "%s: Interface: %s is not configured for pim",
1747 up
->rpf
.source_nexthop
.interface
->name
);
1750 up
->reg_state
= PIM_REG_JOIN_PENDING
;
1751 pim_upstream_start_register_stop_timer(up
, 1);
1753 if (((up
->channel_oil
->cc
.lastused
/ 100)
1754 > pim
->keep_alive_time
)
1755 && (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
))) {
1756 if (PIM_DEBUG_PIM_TRACE
)
1758 "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
1762 pim_null_register_send(up
);
1764 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__
);
1956 rb_pim_upstream_fini(&pim
->upstream_head
);
1958 if (pim
->upstream_sg_wheel
)
1959 wheel_delete(pim
->upstream_sg_wheel
);
1960 pim
->upstream_sg_wheel
= NULL
;
1963 bool pim_upstream_equal(const void *arg1
, const void *arg2
)
1965 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
1966 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
1968 return !pim_sgaddr_cmp(up1
->sg
, up2
->sg
);
1971 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1972 * the cases where kat has to be restarted on rxing traffic -
1974 * if( DirectlyConnected(S) == true AND iif == RPF_interface(S) ) {
1975 * set KeepaliveTimer(S,G) to Keepalive_Period
1976 * # Note: a register state transition or UpstreamJPState(S,G)
1977 * # transition may happen as a result of restarting
1978 * # KeepaliveTimer, and must be dealt with here.
1980 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1981 * inherited_olist(S,G) != NULL ) {
1982 * set KeepaliveTimer(S,G) to Keepalive_Period
1985 static bool pim_upstream_kat_start_ok(struct pim_upstream
*up
)
1987 struct channel_oil
*c_oil
= up
->channel_oil
;
1988 struct interface
*ifp
= up
->rpf
.source_nexthop
.interface
;
1989 struct pim_interface
*pim_ifp
;
1991 /* "iif == RPF_interface(S)" check is not easy to do as the info
1992 * we get from the kernel/ASIC is really a "lookup/key hit".
1993 * So we will do an approximate check here to avoid starting KAT
1994 * because of (S,G,rpt) forwarding on a non-LHR.
1999 pim_ifp
= ifp
->info
;
2000 if (pim_ifp
->mroute_vif_index
!= c_oil
->oil
.mfcc_parent
)
2003 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
2008 if ((up
->join_state
== PIM_UPSTREAM_JOINED
)
2009 && !pim_upstream_empty_inherited_olist(up
)) {
2016 static bool pim_upstream_sg_running_proc(struct pim_upstream
*up
)
2019 struct pim_instance
*pim
= up
->pim
;
2021 if (!up
->channel_oil
->installed
)
2024 pim_mroute_update_counters(up
->channel_oil
);
2026 // Have we seen packets?
2027 if ((up
->channel_oil
->cc
.oldpktcnt
>= up
->channel_oil
->cc
.pktcnt
)
2028 && (up
->channel_oil
->cc
.lastused
/ 100 > 30)) {
2029 if (PIM_DEBUG_PIM_TRACE
) {
2031 "%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
2032 __func__
, up
->sg_str
, pim
->vrf
->name
,
2033 up
->channel_oil
->cc
.oldpktcnt
,
2034 up
->channel_oil
->cc
.pktcnt
,
2035 up
->channel_oil
->cc
.lastused
/ 100);
2040 if (pim_upstream_kat_start_ok(up
)) {
2041 /* Add a source reference to the stream if
2042 * one doesn't already exist */
2043 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
2044 if (PIM_DEBUG_PIM_TRACE
)
2046 "source reference created on kat restart %s[%s]",
2047 up
->sg_str
, pim
->vrf
->name
);
2049 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM
,
2051 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
2052 pim_upstream_fhr_kat_start(up
);
2054 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
2056 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
2057 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
2061 if ((up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
) &&
2062 (up
->rpf
.source_nexthop
.interface
)) {
2063 pim_upstream_set_sptbit(up
, up
->rpf
.source_nexthop
.interface
);
2070 * Code to check and see if we've received packets on a S,G mroute
2071 * and if so to set the SPT bit appropriately
2073 static void pim_upstream_sg_running(void *arg
)
2075 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
2076 struct pim_instance
*pim
= up
->channel_oil
->pim
;
2078 // No packet can have arrived here if this is the case
2079 if (!up
->channel_oil
->installed
) {
2080 if (PIM_DEBUG_TRACE
)
2081 zlog_debug("%s: %s%s is not installed in mroute",
2082 __func__
, up
->sg_str
, pim
->vrf
->name
);
2087 * This is a bit of a hack
2088 * We've noted that we should rescan but
2089 * we've missed the window for doing so in
2090 * pim_zebra.c for some reason. I am
2091 * only doing this at this point in time
2092 * to get us up and working for the moment
2094 if (up
->channel_oil
->oil_inherited_rescan
) {
2095 if (PIM_DEBUG_TRACE
)
2097 "%s: Handling unscanned inherited_olist for %s[%s]",
2098 __func__
, up
->sg_str
, pim
->vrf
->name
);
2099 pim_upstream_inherited_olist_decide(pim
, up
);
2100 up
->channel_oil
->oil_inherited_rescan
= 0;
2103 pim_upstream_sg_running_proc(up
);
2106 void pim_upstream_add_lhr_star_pimreg(struct pim_instance
*pim
)
2108 struct pim_upstream
*up
;
2110 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
2111 if (!pim_addr_is_any(up
->sg
.src
))
2114 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up
->flags
))
2117 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
2118 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2122 void pim_upstream_spt_prefix_list_update(struct pim_instance
*pim
,
2123 struct prefix_list
*pl
)
2125 const char *pname
= prefix_list_name(pl
);
2127 if (pim
->spt
.plist
&& strcmp(pim
->spt
.plist
, pname
) == 0) {
2128 pim_upstream_remove_lhr_star_pimreg(pim
, pname
);
2133 * nlist -> The new prefix list
2135 * Per Group Application of pimreg to the OIL
2136 * If the prefix list tells us DENY then
2137 * we need to Switchover to SPT immediate
2138 * so add the pimreg.
2139 * If the prefix list tells us to ACCEPT than
2140 * we need to Never do the SPT so remove
2144 void pim_upstream_remove_lhr_star_pimreg(struct pim_instance
*pim
,
2147 struct pim_upstream
*up
;
2148 struct prefix_list
*np
;
2150 enum prefix_list_type apply_new
;
2152 np
= prefix_list_lookup(PIM_AFI
, nlist
);
2154 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
2155 if (!pim_addr_is_any(up
->sg
.src
))
2158 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up
->flags
))
2162 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
2163 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2166 pim_addr_to_prefix(&g
, up
->sg
.grp
);
2167 apply_new
= prefix_list_apply(np
, &g
);
2168 if (apply_new
== PREFIX_DENY
)
2169 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
2170 PIM_OIF_FLAG_PROTO_IGMP
,
2173 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
2174 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
2178 void pim_upstream_init(struct pim_instance
*pim
)
2182 snprintf(name
, sizeof(name
), "PIM %s Timer Wheel", pim
->vrf
->name
);
2183 pim
->upstream_sg_wheel
=
2184 wheel_init(router
->master
, 31000, 100, pim_upstream_hash_key
,
2185 pim_upstream_sg_running
, name
);
2187 rb_pim_upstream_init(&pim
->upstream_head
);