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
22 #include "zebra/rib.h"
39 #include "pim_iface.h"
41 #include "pim_zlookup.h"
42 #include "pim_upstream.h"
43 #include "pim_ifchannel.h"
44 #include "pim_neighbor.h"
46 #include "pim_zebra.h"
48 #include "pim_macro.h"
51 #include "pim_register.h"
53 #include "pim_jp_agg.h"
57 struct hash
*pim_upstream_hash
= NULL
;
58 struct list
*pim_upstream_list
= NULL
;
59 struct timer_wheel
*pim_upstream_sg_wheel
= NULL
;
61 static void join_timer_stop(struct pim_upstream
*up
);
63 pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
);
66 * A (*,G) or a (*,*) is going away
67 * remove the parent pointer from
68 * those pointing at us
70 static void pim_upstream_remove_children(struct pim_upstream
*up
)
72 struct pim_upstream
*child
;
77 while (!list_isempty(up
->sources
)) {
78 child
= listnode_head(up
->sources
);
79 listnode_delete(up
->sources
, child
);
80 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child
->flags
)) {
81 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child
->flags
);
82 child
= pim_upstream_del(child
, __PRETTY_FUNCTION__
);
87 list_delete(up
->sources
);
92 * A (*,G) or a (*,*) is being created
93 * Find the children that would point
96 static void pim_upstream_find_new_children(struct pim_upstream
*up
)
98 struct pim_upstream
*child
;
99 struct listnode
*ch_node
;
101 if ((up
->sg
.src
.s_addr
!= INADDR_ANY
)
102 && (up
->sg
.grp
.s_addr
!= INADDR_ANY
))
105 if ((up
->sg
.src
.s_addr
== INADDR_ANY
)
106 && (up
->sg
.grp
.s_addr
== INADDR_ANY
))
109 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, ch_node
, child
)) {
110 if ((up
->sg
.grp
.s_addr
!= INADDR_ANY
)
111 && (child
->sg
.grp
.s_addr
== up
->sg
.grp
.s_addr
)
114 listnode_add_sort(up
->sources
, child
);
120 * If we have a (*,*) || (S,*) there is no parent
121 * If we have a (S,G), find the (*,G)
122 * If we have a (*,G), find the (*,*)
124 static struct pim_upstream
*pim_upstream_find_parent(struct pim_upstream
*child
)
126 struct prefix_sg any
= child
->sg
;
127 struct pim_upstream
*up
= NULL
;
130 if ((child
->sg
.src
.s_addr
!= INADDR_ANY
)
131 && (child
->sg
.grp
.s_addr
!= INADDR_ANY
)) {
132 any
.src
.s_addr
= INADDR_ANY
;
133 up
= pim_upstream_find(&any
);
136 listnode_add(up
->sources
, child
);
144 void pim_upstream_free(struct pim_upstream
*up
)
146 XFREE(MTYPE_PIM_UPSTREAM
, up
);
150 static void upstream_channel_oil_detach(struct pim_upstream
*up
)
152 if (up
->channel_oil
) {
153 /* Detaching from channel_oil, channel_oil may exist post del,
154 but upstream would not keep reference of it
156 pim_channel_oil_del(up
->channel_oil
);
157 up
->channel_oil
= NULL
;
161 struct pim_upstream
*pim_upstream_del(struct pim_upstream
*up
, const char *name
)
163 bool notify_msdp
= false;
168 "%s(%s): Delete %s ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
169 __PRETTY_FUNCTION__
, name
, up
->sg_str
, up
->ref_count
,
170 up
->flags
, up
->channel_oil
->oil_ref_count
);
174 if (up
->ref_count
>= 1)
177 THREAD_OFF(up
->t_ka_timer
);
178 THREAD_OFF(up
->t_rs_timer
);
179 THREAD_OFF(up
->t_msdp_reg_timer
);
181 if (up
->join_state
== PIM_UPSTREAM_JOINED
) {
182 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0);
184 if (up
->sg
.src
.s_addr
== INADDR_ANY
) {
185 /* if a (*, G) entry in the joined state is being
187 * need to notify MSDP */
193 pim_jp_agg_upstream_verification(up
, false);
194 up
->rpf
.source_nexthop
.interface
= NULL
;
196 if (up
->sg
.src
.s_addr
!= INADDR_ANY
) {
197 wheel_remove_item(pim_upstream_sg_wheel
, up
);
201 pim_upstream_remove_children(up
);
203 list_delete(up
->sources
);
205 pim_mroute_del(up
->channel_oil
, __PRETTY_FUNCTION__
);
206 upstream_channel_oil_detach(up
);
208 list_delete(up
->ifchannels
);
209 up
->ifchannels
= NULL
;
212 notice that listnode_delete() can't be moved
213 into pim_upstream_free() because the later is
214 called by list_delete_all_node()
216 if (up
->parent
&& up
->parent
->sources
)
217 listnode_delete(up
->parent
->sources
, up
);
220 listnode_delete(pim_upstream_list
, up
);
221 hash_release(pim_upstream_hash
, up
);
224 pim_msdp_up_del(&up
->sg
);
227 /* Deregister addr with Zebra NHT */
228 nht_p
.family
= AF_INET
;
229 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
230 nht_p
.u
.prefix4
= up
->upstream_addr
;
231 if (PIM_DEBUG_TRACE
) {
232 char buf
[PREFIX2STR_BUFFER
];
233 prefix2str(&nht_p
, buf
, sizeof(buf
));
234 zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT",
235 __PRETTY_FUNCTION__
, up
->sg_str
, buf
);
237 pim_delete_tracked_nexthop(&nht_p
, up
, NULL
);
239 pim_upstream_free(up
);
244 void pim_upstream_send_join(struct pim_upstream
*up
)
246 if (PIM_DEBUG_TRACE
) {
247 char rpf_str
[PREFIX_STRLEN
];
248 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
250 zlog_debug("%s: RPF'%s=%s(%s) for Interface %s",
251 __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
,
252 pim_upstream_state2str(up
->join_state
),
253 up
->rpf
.source_nexthop
.interface
->name
);
254 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
255 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
256 __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
);
261 /* send Join(S,G) to the current upstream neighbor */
262 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 1 /* join */);
265 static int on_join_timer(struct thread
*t
)
267 struct pim_upstream
*up
;
272 * In the case of a HFR we will not ahve anyone to send this to.
274 if (PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
278 * Don't send the join if the outgoing interface is a loopback
279 * But since this might change leave the join timer running
281 if (up
->rpf
.source_nexthop
282 .interface
&& !if_is_loopback(up
->rpf
.source_nexthop
.interface
))
283 pim_upstream_send_join(up
);
285 join_timer_start(up
);
290 static void join_timer_stop(struct pim_upstream
*up
)
292 struct pim_neighbor
*nbr
;
294 THREAD_OFF(up
->t_join_timer
);
296 nbr
= pim_neighbor_find(up
->rpf
.source_nexthop
.interface
,
297 up
->rpf
.rpf_addr
.u
.prefix4
);
300 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
);
302 pim_jp_agg_upstream_verification(up
, false);
305 void join_timer_start(struct pim_upstream
*up
)
307 struct pim_neighbor
*nbr
= NULL
;
309 if (up
->rpf
.source_nexthop
.interface
) {
310 nbr
= pim_neighbor_find(up
->rpf
.source_nexthop
.interface
,
311 up
->rpf
.rpf_addr
.u
.prefix4
);
313 if (PIM_DEBUG_PIM_EVENTS
) {
315 "%s: starting %d sec timer for upstream (S,G)=%s",
316 __PRETTY_FUNCTION__
, qpim_t_periodic
,
322 pim_jp_agg_add_group(nbr
->upstream_jp_agg
, up
, 1);
324 THREAD_OFF(up
->t_join_timer
);
325 thread_add_timer(master
, on_join_timer
, up
, qpim_t_periodic
,
328 pim_jp_agg_upstream_verification(up
, true);
332 * This is only called when we are switching the upstream
333 * J/P from one neighbor to another
335 * As such we need to remove from the old list and
336 * add to the new list.
338 void pim_upstream_join_timer_restart(struct pim_upstream
*up
,
341 // THREAD_OFF(up->t_join_timer);
342 join_timer_start(up
);
345 static void pim_upstream_join_timer_restart_msec(struct pim_upstream
*up
,
348 if (PIM_DEBUG_PIM_EVENTS
) {
349 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
350 __PRETTY_FUNCTION__
, interval_msec
, up
->sg_str
);
353 THREAD_OFF(up
->t_join_timer
);
354 thread_add_timer_msec(master
, on_join_timer
, up
, interval_msec
,
358 void pim_upstream_join_suppress(struct pim_upstream
*up
,
359 struct in_addr rpf_addr
, int holdtime
)
361 long t_joinsuppress_msec
;
362 long join_timer_remain_msec
;
364 t_joinsuppress_msec
=
365 MIN(pim_if_t_suppressed_msec(up
->rpf
.source_nexthop
.interface
),
368 join_timer_remain_msec
= pim_time_timer_remain_msec(up
->t_join_timer
);
370 if (PIM_DEBUG_TRACE
) {
371 char rpf_str
[INET_ADDRSTRLEN
];
372 pim_inet4_dump("<rpf?>", rpf_addr
, rpf_str
, sizeof(rpf_str
));
374 "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
375 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
,
376 join_timer_remain_msec
, t_joinsuppress_msec
);
379 if (join_timer_remain_msec
< t_joinsuppress_msec
) {
380 if (PIM_DEBUG_TRACE
) {
382 "%s %s: suppressing Join(S,G)=%s for %ld msec",
383 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
,
384 t_joinsuppress_msec
);
387 pim_upstream_join_timer_restart_msec(up
, t_joinsuppress_msec
);
391 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label
,
392 struct pim_upstream
*up
)
394 long join_timer_remain_msec
;
397 join_timer_remain_msec
= pim_time_timer_remain_msec(up
->t_join_timer
);
399 pim_if_t_override_msec(up
->rpf
.source_nexthop
.interface
);
401 if (PIM_DEBUG_TRACE
) {
402 char rpf_str
[INET_ADDRSTRLEN
];
403 pim_inet4_dump("<rpf?>", up
->rpf
.rpf_addr
.u
.prefix4
, rpf_str
,
406 "%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
407 debug_label
, up
->sg_str
, rpf_str
,
408 join_timer_remain_msec
, t_override_msec
);
411 if (join_timer_remain_msec
> t_override_msec
) {
412 if (PIM_DEBUG_TRACE
) {
414 "%s: decreasing (S,G)=%s join timer to t_override=%d msec",
415 debug_label
, up
->sg_str
, t_override_msec
);
418 pim_upstream_join_timer_restart_msec(up
, t_override_msec
);
422 static void forward_on(struct pim_upstream
*up
)
424 struct listnode
*chnode
;
425 struct listnode
*chnextnode
;
426 struct pim_ifchannel
*ch
= NULL
;
428 /* scan (S,G) state */
429 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
430 if (pim_macro_chisin_oiflist(ch
))
431 pim_forward_start(ch
);
433 } /* scan iface channel list */
436 static void forward_off(struct pim_upstream
*up
)
438 struct listnode
*chnode
;
439 struct listnode
*chnextnode
;
440 struct pim_ifchannel
*ch
;
442 /* scan per-interface (S,G) state */
443 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
445 pim_forward_stop(ch
);
447 } /* scan iface channel list */
450 static int pim_upstream_could_register(struct pim_upstream
*up
)
452 struct pim_interface
*pim_ifp
= NULL
;
454 if (up
->rpf
.source_nexthop
.interface
)
455 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
458 zlog_debug("%s: up %s RPF is not present",
459 __PRETTY_FUNCTION__
, up
->sg_str
);
462 if (pim_ifp
&& PIM_I_am_DR(pim_ifp
)
463 && pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
470 /* Source registration is supressed for SSM groups. When the SSM range changes
471 * we re-revaluate register setup for existing upstream entries */
472 void pim_upstream_register_reevaluate(void)
474 struct listnode
*upnode
;
475 struct pim_upstream
*up
;
477 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, upnode
, up
)) {
478 /* If FHR is set CouldRegister is True. Also check if the flow
479 * is actually active; if it is not kat setup will trigger
481 * registration whenever the flow becomes active. */
482 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) || !up
->t_ka_timer
)
485 if (pim_is_grp_ssm(up
->sg
.grp
)) {
486 /* clear the register state for SSM groups */
487 if (up
->reg_state
!= PIM_REG_NOINFO
) {
488 if (PIM_DEBUG_PIM_EVENTS
)
490 "Clear register for %s as G is now SSM",
492 /* remove regiface from the OIL if it is there*/
493 pim_channel_del_oif(up
->channel_oil
,
495 PIM_OIF_FLAG_PROTO_PIM
);
496 up
->reg_state
= PIM_REG_NOINFO
;
499 /* register ASM sources with the RP */
500 if (up
->reg_state
== PIM_REG_NOINFO
) {
501 if (PIM_DEBUG_PIM_EVENTS
)
503 "Register %s as G is now ASM",
505 pim_channel_add_oif(up
->channel_oil
,
507 PIM_OIF_FLAG_PROTO_PIM
);
508 up
->reg_state
= PIM_REG_JOIN
;
514 void pim_upstream_switch(struct pim_upstream
*up
,
515 enum pim_upstream_state new_state
)
517 enum pim_upstream_state old_state
= up
->join_state
;
519 if (PIM_DEBUG_PIM_EVENTS
) {
520 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
521 __PRETTY_FUNCTION__
, up
->sg_str
,
522 pim_upstream_state2str(up
->join_state
),
523 pim_upstream_state2str(new_state
));
526 up
->join_state
= new_state
;
527 if (old_state
!= new_state
)
528 up
->state_transition
= pim_time_monotonic_sec();
530 pim_upstream_update_assert_tracking_desired(up
);
532 if (new_state
== PIM_UPSTREAM_JOINED
) {
533 if (old_state
!= PIM_UPSTREAM_JOINED
) {
534 int old_fhr
= PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
);
536 pim_msdp_up_join_state_changed(up
);
537 if (pim_upstream_could_register(up
)) {
538 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
540 && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(
542 pim_upstream_keep_alive_timer_start(
543 up
, qpim_keep_alive_time
);
544 pim_register_join(up
);
547 pim_upstream_send_join(up
);
548 join_timer_start(up
);
556 if (old_state
== PIM_UPSTREAM_JOINED
)
557 pim_msdp_up_join_state_changed(up
);
559 /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
561 If I am RP for G then send S,G prune to its IIF. */
562 if (pim_upstream_is_sg_rpt(up
) && up
->parent
563 && !I_am_RP(up
->sg
.grp
)) {
564 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
566 "%s: *,G IIF %s S,G IIF %s ",
568 up
->parent
->rpf
.source_nexthop
570 up
->rpf
.source_nexthop
.interface
->name
);
571 pim_jp_agg_single_upstream_send(&up
->parent
->rpf
,
575 pim_jp_agg_single_upstream_send(&up
->rpf
, up
,
581 int pim_upstream_compare(void *arg1
, void *arg2
)
583 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
584 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
586 if (ntohl(up1
->sg
.grp
.s_addr
) < ntohl(up2
->sg
.grp
.s_addr
))
589 if (ntohl(up1
->sg
.grp
.s_addr
) > ntohl(up2
->sg
.grp
.s_addr
))
592 if (ntohl(up1
->sg
.src
.s_addr
) < ntohl(up2
->sg
.src
.s_addr
))
595 if (ntohl(up1
->sg
.src
.s_addr
) > ntohl(up2
->sg
.src
.s_addr
))
601 static struct pim_upstream
*
602 pim_upstream_new(struct prefix_sg
*sg
, struct interface
*incoming
, int flags
)
604 enum pim_rpf_result rpf_result
;
605 struct pim_interface
*pim_ifp
;
606 struct pim_upstream
*up
;
608 up
= XCALLOC(MTYPE_PIM_UPSTREAM
, sizeof(*up
));
610 zlog_err("%s: PIM XCALLOC(%zu) failure", __PRETTY_FUNCTION__
,
616 pim_str_sg_set(sg
, up
->sg_str
);
617 up
= hash_get(pim_upstream_hash
, up
, hash_alloc_intern
);
618 if (!pim_rp_set_upstream_addr(&up
->upstream_addr
, sg
->src
, sg
->grp
)) {
620 zlog_debug("%s: Received a (*,G) with no RP configured",
621 __PRETTY_FUNCTION__
);
623 hash_release(pim_upstream_hash
, up
);
624 XFREE(MTYPE_PIM_UPSTREAM
, up
);
628 up
->parent
= pim_upstream_find_parent(up
);
629 if (up
->sg
.src
.s_addr
== INADDR_ANY
) {
630 up
->sources
= list_new();
631 up
->sources
->cmp
= pim_upstream_compare
;
635 pim_upstream_find_new_children(up
);
638 up
->t_join_timer
= NULL
;
639 up
->t_ka_timer
= NULL
;
640 up
->t_rs_timer
= NULL
;
641 up
->t_msdp_reg_timer
= NULL
;
642 up
->join_state
= PIM_UPSTREAM_NOTJOINED
;
643 up
->reg_state
= PIM_REG_NOINFO
;
644 up
->state_transition
= pim_time_monotonic_sec();
645 up
->channel_oil
= NULL
;
646 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
648 up
->rpf
.source_nexthop
.interface
= NULL
;
649 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.family
= AF_INET
;
650 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
.s_addr
=
652 up
->rpf
.source_nexthop
.mrib_metric_preference
=
653 qpim_infinite_assert_metric
.metric_preference
;
654 up
->rpf
.source_nexthop
.mrib_route_metric
=
655 qpim_infinite_assert_metric
.route_metric
;
656 up
->rpf
.rpf_addr
.family
= AF_INET
;
657 up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
= PIM_NET_INADDR_ANY
;
659 up
->ifchannels
= list_new();
660 up
->ifchannels
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
662 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
663 wheel_add_item(pim_upstream_sg_wheel
, up
);
665 rpf_result
= pim_rpf_update(up
, NULL
, 1);
666 if (rpf_result
== PIM_RPF_FAILURE
) {
671 "%s: Attempting to create upstream(%s), Unable to RPF for source",
672 __PRETTY_FUNCTION__
, up
->sg_str
);
674 nht_p
.family
= AF_INET
;
675 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
676 nht_p
.u
.prefix4
= up
->upstream_addr
;
677 pim_delete_tracked_nexthop(&nht_p
, up
, NULL
);
680 listnode_delete(up
->parent
->sources
, up
);
684 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
685 wheel_remove_item(pim_upstream_sg_wheel
, up
);
687 pim_upstream_remove_children(up
);
689 list_delete(up
->sources
);
691 hash_release(pim_upstream_hash
, up
);
692 XFREE(MTYPE_PIM_UPSTREAM
, up
);
696 if (up
->rpf
.source_nexthop
.interface
) {
697 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
699 up
->channel_oil
= pim_channel_oil_add(
700 &up
->sg
, pim_ifp
->mroute_vif_index
);
702 listnode_add_sort(pim_upstream_list
, up
);
704 if (PIM_DEBUG_TRACE
) {
706 "%s: Created Upstream %s upstream_addr %s ref count %d increment",
707 __PRETTY_FUNCTION__
, up
->sg_str
,
708 inet_ntoa(up
->upstream_addr
), up
->ref_count
);
714 struct pim_upstream
*pim_upstream_find(struct prefix_sg
*sg
)
716 struct pim_upstream lookup
;
717 struct pim_upstream
*up
= NULL
;
720 up
= hash_lookup(pim_upstream_hash
, &lookup
);
724 struct pim_upstream
*pim_upstream_find_or_add(struct prefix_sg
*sg
,
725 struct interface
*incoming
,
726 int flags
, const char *name
)
728 struct pim_upstream
*up
;
730 up
= pim_upstream_find(sg
);
733 if (!(up
->flags
& flags
)) {
738 "%s(%s): upstream %s ref count %d increment",
739 __PRETTY_FUNCTION__
, name
, up
->sg_str
,
743 up
= pim_upstream_add(sg
, incoming
, flags
, name
);
748 void pim_upstream_ref(struct pim_upstream
*up
, int flags
, const char *name
)
753 zlog_debug("%s(%s): upstream %s ref count %d increment",
754 __PRETTY_FUNCTION__
, name
, up
->sg_str
,
758 struct pim_upstream
*pim_upstream_add(struct prefix_sg
*sg
,
759 struct interface
*incoming
, int flags
,
762 struct pim_upstream
*up
= NULL
;
764 up
= pim_upstream_find(sg
);
766 pim_upstream_ref(up
, flags
, name
);
769 up
= pim_upstream_new(sg
, incoming
, flags
);
772 if (PIM_DEBUG_TRACE
) {
774 char buf
[PREFIX2STR_BUFFER
];
775 prefix2str(&up
->rpf
.rpf_addr
, buf
, sizeof(buf
));
776 zlog_debug("%s(%s): %s, iif %s (%s) found: %d: ref_count: %d",
777 __PRETTY_FUNCTION__
, name
,
778 up
->sg_str
, buf
, up
->rpf
.source_nexthop
.interface
?
779 up
->rpf
.source_nexthop
.interface
->name
: "NIL" ,
780 found
, up
->ref_count
);
782 zlog_debug("%s(%s): (%s) failure to create",
783 __PRETTY_FUNCTION__
, name
,
784 pim_str_sg_dump(sg
));
791 * Passed in up must be the upstream for ch. starch is NULL if no
794 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream
*up
,
795 struct pim_ifchannel
*ch
,
796 struct pim_ifchannel
*starch
)
799 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
802 if (!pim_macro_ch_lost_assert(ch
)
803 && pim_macro_chisin_joins_or_include(ch
))
811 if (PIM_IF_FLAG_TEST_S_G_RPT(starch
->upstream
->flags
))
814 if (!pim_macro_ch_lost_assert(starch
)
815 && pim_macro_chisin_joins_or_include(starch
))
823 Evaluate JoinDesired(S,G):
825 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
828 inherited_olist(S,G) =
829 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
831 JoinDesired(S,G) may be affected by changes in the following:
833 pim_ifp->primary_address
835 ch->ifassert_winner_metric
837 ch->local_ifmembership
839 ch->upstream->rpf.source_nexthop.mrib_metric_preference
840 ch->upstream->rpf.source_nexthop.mrib_route_metric
841 ch->upstream->rpf.source_nexthop.interface
843 See also pim_upstream_update_join_desired() below.
845 int pim_upstream_evaluate_join_desired(struct pim_upstream
*up
)
847 struct interface
*ifp
;
848 struct listnode
*node
;
849 struct pim_ifchannel
*ch
, *starch
;
850 struct pim_upstream
*starup
= up
->parent
;
853 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT
), node
, ifp
)) {
857 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
860 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
867 ret
+= pim_upstream_evaluate_join_desired_interface(up
, ch
,
869 } /* scan iface channel list */
871 return ret
; /* false */
875 See also pim_upstream_evaluate_join_desired() above.
877 void pim_upstream_update_join_desired(struct pim_upstream
*up
)
879 int was_join_desired
; /* boolean */
880 int is_join_desired
; /* boolean */
882 was_join_desired
= PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up
->flags
);
884 is_join_desired
= pim_upstream_evaluate_join_desired(up
);
886 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up
->flags
);
888 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up
->flags
);
890 /* switched from false to true */
891 if (is_join_desired
&& !was_join_desired
) {
892 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
896 /* switched from true to false */
897 if (!is_join_desired
&& was_join_desired
) {
898 pim_upstream_switch(up
, PIM_UPSTREAM_NOTJOINED
);
904 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
905 Transitions from Joined State
906 RPF'(S,G) GenID changes
908 The upstream (S,G) state machine remains in Joined state. If the
909 Join Timer is set to expire in more than t_override seconds, reset
910 it so that it expires after t_override seconds.
912 void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr
)
914 struct listnode
*up_node
;
915 struct listnode
*up_nextnode
;
916 struct pim_upstream
*up
;
919 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
921 for (ALL_LIST_ELEMENTS(pim_upstream_list
, up_node
, up_nextnode
, up
)) {
923 if (PIM_DEBUG_TRACE
) {
924 char neigh_str
[INET_ADDRSTRLEN
];
925 char rpf_addr_str
[PREFIX_STRLEN
];
926 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
928 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_addr_str
,
929 sizeof(rpf_addr_str
));
931 "%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
932 __PRETTY_FUNCTION__
, neigh_str
, up
->sg_str
,
933 up
->join_state
== PIM_UPSTREAM_JOINED
,
937 /* consider only (S,G) upstream in Joined state */
938 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
941 /* match RPF'(S,G)=neigh_addr */
942 if (up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
!= neigh_addr
.s_addr
)
945 pim_upstream_join_timer_decrease_to_t_override(
946 "RPF'(S,G) GenID change", up
);
951 void pim_upstream_rpf_interface_changed(struct pim_upstream
*up
,
952 struct interface
*old_rpf_ifp
)
954 struct listnode
*chnode
;
955 struct listnode
*chnextnode
;
956 struct pim_ifchannel
*ch
;
958 /* search all ifchannels */
959 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
960 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
962 /* RPF_interface(S) was NOT I */
963 (old_rpf_ifp
== ch
->interface
) &&
964 /* RPF_interface(S) stopped being I */
965 (ch
->upstream
->rpf
.source_nexthop
966 .interface
!= ch
->interface
)) {
967 assert_action_a5(ch
);
969 } /* PIM_IFASSERT_I_AM_LOSER */
971 pim_ifchannel_update_assert_tracking_desired(ch
);
975 void pim_upstream_update_could_assert(struct pim_upstream
*up
)
977 struct listnode
*chnode
;
978 struct listnode
*chnextnode
;
979 struct pim_ifchannel
*ch
;
981 /* scan per-interface (S,G) state */
982 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
983 pim_ifchannel_update_could_assert(ch
);
984 } /* scan iface channel list */
987 void pim_upstream_update_my_assert_metric(struct pim_upstream
*up
)
989 struct listnode
*chnode
;
990 struct listnode
*chnextnode
;
991 struct pim_ifchannel
*ch
;
993 /* scan per-interface (S,G) state */
994 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
995 pim_ifchannel_update_my_assert_metric(ch
);
997 } /* scan iface channel list */
1000 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
)
1002 struct listnode
*chnode
;
1003 struct listnode
*chnextnode
;
1004 struct pim_interface
*pim_ifp
;
1005 struct pim_ifchannel
*ch
;
1007 /* scan per-interface (S,G) state */
1008 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1011 pim_ifp
= ch
->interface
->info
;
1015 pim_ifchannel_update_assert_tracking_desired(ch
);
1017 } /* scan iface channel list */
1020 /* When kat is stopped CouldRegister goes to false so we need to
1021 * transition the (S, G) on FHR to NI state and remove reg tunnel
1023 static void pim_upstream_fhr_kat_expiry(struct pim_upstream
*up
)
1025 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
1028 if (PIM_DEBUG_TRACE
)
1029 zlog_debug("kat expired on %s; clear fhr reg state",
1032 /* stop reg-stop timer */
1033 THREAD_OFF(up
->t_rs_timer
);
1034 /* remove regiface from the OIL if it is there*/
1035 pim_channel_del_oif(up
->channel_oil
, pim_regiface
,
1036 PIM_OIF_FLAG_PROTO_PIM
);
1037 /* clear the register state */
1038 up
->reg_state
= PIM_REG_NOINFO
;
1039 PIM_UPSTREAM_FLAG_UNSET_FHR(up
->flags
);
1042 /* When kat is started CouldRegister can go to true. And if it does we
1043 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1045 static void pim_upstream_fhr_kat_start(struct pim_upstream
*up
)
1047 if (pim_upstream_could_register(up
)) {
1048 if (PIM_DEBUG_TRACE
)
1050 "kat started on %s; set fhr reg state to joined",
1053 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1054 if (up
->reg_state
== PIM_REG_NOINFO
)
1055 pim_register_join(up
);
1060 * On an RP, the PMBR value must be cleared when the
1061 * Keepalive Timer expires
1062 * KAT expiry indicates that flow is inactive. If the flow was created or
1063 * maintained by activity now is the time to deref it.
1065 static int pim_upstream_keep_alive_timer(struct thread
*t
)
1067 struct pim_upstream
*up
;
1071 if (I_am_RP(up
->sg
.grp
)) {
1072 pim_br_clear_pmbr(&up
->sg
);
1074 * We need to do more here :)
1075 * But this is the start.
1079 /* source is no longer active - pull the SA from MSDP's cache */
1080 pim_msdp_sa_local_del(&up
->sg
);
1082 /* if entry was created because of activity we need to deref it */
1083 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1084 pim_upstream_fhr_kat_expiry(up
);
1085 if (PIM_DEBUG_TRACE
)
1086 zlog_debug("kat expired on %s; remove stream reference",
1088 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up
->flags
);
1089 pim_upstream_del(up
, __PRETTY_FUNCTION__
);
1090 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
1091 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up
->flags
);
1092 pim_upstream_del(up
, __PRETTY_FUNCTION__
);
1098 void pim_upstream_keep_alive_timer_start(struct pim_upstream
*up
, uint32_t time
)
1100 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1101 if (PIM_DEBUG_TRACE
)
1102 zlog_debug("kat start on %s with no stream reference",
1105 THREAD_OFF(up
->t_ka_timer
);
1106 thread_add_timer(master
, pim_upstream_keep_alive_timer
, up
, time
,
1109 /* any time keepalive is started against a SG we will have to
1110 * re-evaluate our active source database */
1111 pim_msdp_sa_local_update(up
);
1114 /* MSDP on RP needs to know if a source is registerable to this RP */
1115 static int pim_upstream_msdp_reg_timer(struct thread
*t
)
1117 struct pim_upstream
*up
;
1121 /* source is no longer active - pull the SA from MSDP's cache */
1122 pim_msdp_sa_local_del(&up
->sg
);
1125 void pim_upstream_msdp_reg_timer_start(struct pim_upstream
*up
)
1127 THREAD_OFF(up
->t_msdp_reg_timer
);
1128 thread_add_timer(master
, pim_upstream_msdp_reg_timer
, up
,
1129 PIM_MSDP_REG_RXED_PERIOD
, &up
->t_msdp_reg_timer
);
1131 pim_msdp_sa_local_update(up
);
1135 * 4.2.1 Last-Hop Switchover to the SPT
1137 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1138 * RP. Once traffic from sources to joined groups arrives at a last-hop
1139 * router, it has the option of switching to receive the traffic on a
1140 * shortest path tree (SPT).
1142 * The decision for a router to switch to the SPT is controlled as
1146 * CheckSwitchToSpt(S,G) {
1147 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1148 * (+) pim_include(S,G) != NULL )
1149 * AND SwitchToSptDesired(S,G) ) {
1150 * # Note: Restarting the KAT will result in the SPT switch
1151 * set KeepaliveTimer(S,G) to Keepalive_Period
1155 * SwitchToSptDesired(S,G) is a policy function that is implementation
1156 * defined. An "infinite threshold" policy can be implemented by making
1157 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1158 * first packet" policy can be implemented by making
1159 * SwitchToSptDesired(S,G) return true once a single packet has been
1160 * received for the source and group.
1162 int pim_upstream_switch_to_spt_desired(struct prefix_sg
*sg
)
1164 if (I_am_RP(sg
->grp
))
1170 int pim_upstream_is_sg_rpt(struct pim_upstream
*up
)
1172 struct listnode
*chnode
;
1173 struct pim_ifchannel
*ch
;
1175 for (ALL_LIST_ELEMENTS_RO(up
->ifchannels
, chnode
, ch
)) {
1176 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1183 * After receiving a packet set SPTbit:
1185 * Update_SPTbit(S,G,iif) {
1186 * if ( iif == RPF_interface(S)
1187 * AND JoinDesired(S,G) == TRUE
1188 * AND ( DirectlyConnected(S) == TRUE
1189 * OR RPF_interface(S) != RPF_interface(RP(G))
1190 * OR inherited_olist(S,G,rpt) == NULL
1191 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1192 * ( RPF'(S,G) != NULL ) )
1193 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1194 * Set SPTbit(S,G) to TRUE
1198 void pim_upstream_set_sptbit(struct pim_upstream
*up
,
1199 struct interface
*incoming
)
1201 struct pim_upstream
*starup
= up
->parent
;
1203 // iif == RPF_interfvace(S)
1204 if (up
->rpf
.source_nexthop
.interface
!= incoming
) {
1205 if (PIM_DEBUG_TRACE
)
1207 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1208 __PRETTY_FUNCTION__
, incoming
->name
,
1209 up
->rpf
.source_nexthop
.interface
->name
);
1213 // AND JoinDesired(S,G) == TRUE
1216 // DirectlyConnected(S) == TRUE
1217 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1219 if (PIM_DEBUG_TRACE
)
1220 zlog_debug("%s: %s is directly connected to the source",
1221 __PRETTY_FUNCTION__
, up
->sg_str
);
1222 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1226 // OR RPF_interface(S) != RPF_interface(RP(G))
1228 || up
->rpf
.source_nexthop
1229 .interface
!= starup
->rpf
.source_nexthop
.interface
) {
1230 if (PIM_DEBUG_TRACE
)
1232 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1233 __PRETTY_FUNCTION__
, up
->sg_str
);
1234 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1238 // OR inherited_olist(S,G,rpt) == NULL
1239 if (pim_upstream_is_sg_rpt(up
)
1240 && pim_upstream_empty_inherited_olist(up
)) {
1241 if (PIM_DEBUG_TRACE
)
1242 zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
1243 __PRETTY_FUNCTION__
, up
->sg_str
);
1244 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1248 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1249 // ( RPF'(S,G) != NULL ) )
1250 if (up
->parent
&& pim_rpf_is_same(&up
->rpf
, &up
->parent
->rpf
)) {
1251 if (PIM_DEBUG_TRACE
)
1252 zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
1253 __PRETTY_FUNCTION__
, up
->sg_str
);
1254 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1261 const char *pim_upstream_state2str(enum pim_upstream_state join_state
)
1263 switch (join_state
) {
1264 case PIM_UPSTREAM_NOTJOINED
:
1267 case PIM_UPSTREAM_JOINED
:
1274 const char *pim_reg_state2str(enum pim_reg_state reg_state
, char *state_str
)
1276 switch (reg_state
) {
1277 case PIM_REG_NOINFO
:
1278 strcpy(state_str
, "RegNoInfo");
1281 strcpy(state_str
, "RegJoined");
1283 case PIM_REG_JOIN_PENDING
:
1284 strcpy(state_str
, "RegJoinPend");
1287 strcpy(state_str
, "RegPrune");
1290 strcpy(state_str
, "RegUnknown");
1295 static int pim_upstream_register_stop_timer(struct thread
*t
)
1297 struct pim_interface
*pim_ifp
;
1298 struct pim_upstream
*up
;
1299 struct pim_rpf
*rpg
;
1303 if (PIM_DEBUG_TRACE
) {
1304 char state_str
[PIM_REG_STATE_STR_LEN
];
1305 zlog_debug("%s: (S,G)=%s upstream register stop timer %s",
1306 __PRETTY_FUNCTION__
, up
->sg_str
,
1307 pim_reg_state2str(up
->reg_state
, state_str
));
1310 switch (up
->reg_state
) {
1311 case PIM_REG_JOIN_PENDING
:
1312 up
->reg_state
= PIM_REG_JOIN
;
1313 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1314 PIM_OIF_FLAG_PROTO_PIM
);
1319 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1321 if (PIM_DEBUG_TRACE
)
1323 "%s: Interface: %s is not configured for pim",
1324 __PRETTY_FUNCTION__
,
1325 up
->rpf
.source_nexthop
.interface
->name
);
1328 up
->reg_state
= PIM_REG_JOIN_PENDING
;
1329 pim_upstream_start_register_stop_timer(up
, 1);
1331 if (((up
->channel_oil
->cc
.lastused
/ 100)
1332 > PIM_KEEPALIVE_PERIOD
)
1333 && (I_am_RP(up
->sg
.grp
))) {
1334 if (PIM_DEBUG_TRACE
)
1336 "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
1337 __PRETTY_FUNCTION__
);
1340 rpg
= RP(up
->sg
.grp
);
1341 memset(&ip_hdr
, 0, sizeof(struct ip
));
1342 ip_hdr
.ip_p
= PIM_IP_PROTO_PIM
;
1345 ip_hdr
.ip_src
= up
->sg
.src
;
1346 ip_hdr
.ip_dst
= up
->sg
.grp
;
1347 ip_hdr
.ip_len
= htons(20);
1348 // checksum is broken
1349 pim_register_send((uint8_t *)&ip_hdr
, sizeof(struct ip
),
1350 pim_ifp
->primary_address
, rpg
, 1, up
);
1359 void pim_upstream_start_register_stop_timer(struct pim_upstream
*up
,
1364 THREAD_TIMER_OFF(up
->t_rs_timer
);
1366 if (!null_register
) {
1367 uint32_t lower
= (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1368 uint32_t upper
= (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1369 time
= lower
+ (random() % (upper
- lower
+ 1))
1370 - PIM_REGISTER_PROBE_PERIOD
;
1372 time
= PIM_REGISTER_PROBE_PERIOD
;
1374 if (PIM_DEBUG_TRACE
) {
1376 "%s: (S,G)=%s Starting upstream register stop timer %d",
1377 __PRETTY_FUNCTION__
, up
->sg_str
, time
);
1379 thread_add_timer(master
, pim_upstream_register_stop_timer
, up
, time
,
1383 int pim_upstream_inherited_olist_decide(struct pim_upstream
*up
)
1385 struct interface
*ifp
;
1386 struct pim_interface
*pim_ifp
= NULL
;
1387 struct pim_ifchannel
*ch
, *starch
;
1388 struct listnode
*node
;
1389 struct pim_upstream
*starup
= up
->parent
;
1390 int output_intf
= 0;
1392 if (up
->rpf
.source_nexthop
.interface
)
1393 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1395 if (PIM_DEBUG_TRACE
)
1396 zlog_debug("%s: up %s RPF is not present",
1397 __PRETTY_FUNCTION__
, up
->sg_str
);
1399 if (pim_ifp
&& !up
->channel_oil
)
1401 pim_channel_oil_add(&up
->sg
, pim_ifp
->mroute_vif_index
);
1403 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT
), node
, ifp
)) {
1407 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1410 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
1417 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1419 int flag
= PIM_OIF_FLAG_PROTO_PIM
;
1422 flag
= PIM_OIF_FLAG_PROTO_STAR
;
1424 pim_channel_add_oif(up
->channel_oil
, ifp
, flag
);
1433 * For a given upstream, determine the inherited_olist
1436 * inherited_olist(S,G,rpt) =
1437 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1438 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1439 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1441 * inherited_olist(S,G) =
1442 * inherited_olist(S,G,rpt) (+)
1443 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1445 * return 1 if there are any output interfaces
1446 * return 0 if there are not any output interfaces
1448 int pim_upstream_inherited_olist(struct pim_upstream
*up
)
1450 int output_intf
= pim_upstream_inherited_olist_decide(up
);
1453 * If we have output_intf switch state to Join and work like normal
1454 * If we don't have an output_intf that means we are probably a
1455 * switch on a stick so turn on forwarding to just accept the
1456 * incoming packets so we don't bother the other stuff!
1459 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
1466 int pim_upstream_empty_inherited_olist(struct pim_upstream
*up
)
1468 return pim_channel_oil_empty(up
->channel_oil
);
1472 * When we have a new neighbor,
1473 * find upstreams that don't have their rpf_addr
1474 * set and see if the new neighbor allows
1475 * the join to be sent
1477 void pim_upstream_find_new_rpf(void)
1479 struct listnode
*up_node
;
1480 struct listnode
*up_nextnode
;
1481 struct pim_upstream
*up
;
1484 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1486 for (ALL_LIST_ELEMENTS(pim_upstream_list
, up_node
, up_nextnode
, up
)) {
1487 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
1488 if (PIM_DEBUG_TRACE
)
1490 "Upstream %s without a path to send join, checking",
1492 pim_rpf_update(up
, NULL
, 1);
1497 static unsigned int pim_upstream_hash_key(void *arg
)
1499 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1501 return jhash_2words(up
->sg
.src
.s_addr
, up
->sg
.grp
.s_addr
, 0);
1504 void pim_upstream_terminate(void)
1506 if (pim_upstream_list
)
1507 list_delete(pim_upstream_list
);
1508 pim_upstream_list
= NULL
;
1510 if (pim_upstream_hash
)
1511 hash_free(pim_upstream_hash
);
1512 pim_upstream_hash
= NULL
;
1515 static int pim_upstream_equal(const void *arg1
, const void *arg2
)
1517 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
1518 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
1520 if ((up1
->sg
.grp
.s_addr
== up2
->sg
.grp
.s_addr
)
1521 && (up1
->sg
.src
.s_addr
== up2
->sg
.src
.s_addr
))
1527 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1528 * the cases where kat has to be restarted on rxing traffic -
1530 * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
1531 * set KeepaliveTimer(S,G) to Keepalive_Period
1532 * # Note: a register state transition or UpstreamJPState(S,G)
1533 * # transition may happen as a result of restarting
1534 * # KeepaliveTimer, and must be dealt with here.
1536 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1537 * inherited_olist(S,G) != NULL ) {
1538 * set KeepaliveTimer(S,G) to Keepalive_Period
1541 static bool pim_upstream_kat_start_ok(struct pim_upstream
*up
)
1543 /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
1544 * so we will skip that here */
1545 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1550 if ((up
->join_state
== PIM_UPSTREAM_JOINED
)
1551 && !pim_upstream_empty_inherited_olist(up
)) {
1552 /* XXX: I have added this RP check just for 3.2 and it's a
1554 * what rfc-4601 says. Till now we were only running KAT on FHR
1556 * there is some angst around making the change to run it all
1558 * maintain the (S, G) state. This is tracked via CM-13601 and
1560 * removed to handle spt turn-arounds correctly in a 3-tier clos
1562 if (I_am_RP(up
->sg
.grp
))
1570 * Code to check and see if we've received packets on a S,G mroute
1571 * and if so to set the SPT bit appropriately
1573 static void pim_upstream_sg_running(void *arg
)
1575 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1577 // No packet can have arrived here if this is the case
1578 if (!up
->channel_oil
|| !up
->channel_oil
->installed
) {
1579 if (PIM_DEBUG_TRACE
)
1580 zlog_debug("%s: %s is not installed in mroute",
1581 __PRETTY_FUNCTION__
, up
->sg_str
);
1586 * This is a bit of a hack
1587 * We've noted that we should rescan but
1588 * we've missed the window for doing so in
1589 * pim_zebra.c for some reason. I am
1590 * only doing this at this point in time
1591 * to get us up and working for the moment
1593 if (up
->channel_oil
->oil_inherited_rescan
) {
1594 if (PIM_DEBUG_TRACE
)
1596 "%s: Handling unscanned inherited_olist for %s",
1597 __PRETTY_FUNCTION__
, up
->sg_str
);
1598 pim_upstream_inherited_olist_decide(up
);
1599 up
->channel_oil
->oil_inherited_rescan
= 0;
1601 pim_mroute_update_counters(up
->channel_oil
);
1603 // Have we seen packets?
1604 if ((up
->channel_oil
->cc
.oldpktcnt
>= up
->channel_oil
->cc
.pktcnt
)
1605 && (up
->channel_oil
->cc
.lastused
/ 100 > 30)) {
1606 if (PIM_DEBUG_TRACE
) {
1608 "%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
1609 __PRETTY_FUNCTION__
, up
->sg_str
,
1610 up
->channel_oil
->cc
.oldpktcnt
,
1611 up
->channel_oil
->cc
.pktcnt
,
1612 up
->channel_oil
->cc
.lastused
/ 100);
1617 if (pim_upstream_kat_start_ok(up
)) {
1618 /* Add a source reference to the stream if
1619 * one doesn't already exist */
1620 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1621 if (PIM_DEBUG_TRACE
)
1623 "source reference created on kat restart %s",
1626 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM
,
1627 __PRETTY_FUNCTION__
);
1628 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
1629 pim_upstream_fhr_kat_start(up
);
1631 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
1632 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
))
1633 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
1635 if (up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
) {
1636 pim_upstream_set_sptbit(up
, up
->rpf
.source_nexthop
.interface
);
1641 void pim_upstream_add_lhr_star_pimreg(void)
1643 struct pim_upstream
*up
;
1644 struct listnode
*node
;
1646 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, node
, up
)) {
1647 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
1650 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
))
1653 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1654 PIM_OIF_FLAG_PROTO_IGMP
);
1658 void pim_upstream_spt_prefix_list_update(struct prefix_list
*pl
)
1660 const char *pname
= prefix_list_name(pl
);
1662 if (pimg
->spt
.plist
&& strcmp(pimg
->spt
.plist
, pname
) == 0) {
1663 pim_upstream_remove_lhr_star_pimreg(pname
);
1668 * nlist -> The new prefix list
1670 * Per Group Application of pimreg to the OIL
1671 * If the prefix list tells us DENY then
1672 * we need to Switchover to SPT immediate
1673 * so add the pimreg.
1674 * If the prefix list tells us to ACCEPT than
1675 * we need to Never do the SPT so remove
1679 void pim_upstream_remove_lhr_star_pimreg(const char *nlist
)
1681 struct pim_upstream
*up
;
1682 struct listnode
*node
;
1683 struct prefix_list
*np
;
1685 enum prefix_list_type apply_new
;
1687 np
= prefix_list_lookup(AFI_IP
, nlist
);
1690 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1692 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, node
, up
)) {
1693 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
1696 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
))
1700 pim_channel_del_oif(up
->channel_oil
, pim_regiface
,
1701 PIM_OIF_FLAG_PROTO_IGMP
);
1704 g
.u
.prefix4
= up
->sg
.grp
;
1705 apply_new
= prefix_list_apply(np
, &g
);
1706 if (apply_new
== PREFIX_DENY
)
1707 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1708 PIM_OIF_FLAG_PROTO_IGMP
);
1710 pim_channel_del_oif(up
->channel_oil
, pim_regiface
,
1711 PIM_OIF_FLAG_PROTO_IGMP
);
1715 void pim_upstream_init(void)
1717 pim_upstream_sg_wheel
=
1718 wheel_init(master
, 31000, 100, pim_upstream_hash_key
,
1719 pim_upstream_sg_running
);
1720 pim_upstream_hash
= hash_create_size(8192, pim_upstream_hash_key
,
1721 pim_upstream_equal
, NULL
);
1723 pim_upstream_list
= list_new();
1724 pim_upstream_list
->del
= (void (*)(void *))pim_upstream_free
;
1725 pim_upstream_list
->cmp
= pim_upstream_compare
;