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(pimg
, &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 pim_ifp
= incoming
->info
;
675 nht_p
.family
= AF_INET
;
676 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
677 nht_p
.u
.prefix4
= up
->upstream_addr
;
678 pim_delete_tracked_nexthop(pim_ifp
->pim
, &nht_p
, up
, NULL
);
681 listnode_delete(up
->parent
->sources
, up
);
685 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
686 wheel_remove_item(pim_upstream_sg_wheel
, up
);
688 pim_upstream_remove_children(up
);
690 list_delete(up
->sources
);
692 hash_release(pim_upstream_hash
, up
);
693 XFREE(MTYPE_PIM_UPSTREAM
, up
);
697 if (up
->rpf
.source_nexthop
.interface
) {
698 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
700 up
->channel_oil
= pim_channel_oil_add(
701 &up
->sg
, pim_ifp
->mroute_vif_index
);
703 listnode_add_sort(pim_upstream_list
, up
);
705 if (PIM_DEBUG_TRACE
) {
707 "%s: Created Upstream %s upstream_addr %s ref count %d increment",
708 __PRETTY_FUNCTION__
, up
->sg_str
,
709 inet_ntoa(up
->upstream_addr
), up
->ref_count
);
715 struct pim_upstream
*pim_upstream_find(struct prefix_sg
*sg
)
717 struct pim_upstream lookup
;
718 struct pim_upstream
*up
= NULL
;
721 up
= hash_lookup(pim_upstream_hash
, &lookup
);
725 struct pim_upstream
*pim_upstream_find_or_add(struct prefix_sg
*sg
,
726 struct interface
*incoming
,
727 int flags
, const char *name
)
729 struct pim_upstream
*up
;
731 up
= pim_upstream_find(sg
);
734 if (!(up
->flags
& flags
)) {
739 "%s(%s): upstream %s ref count %d increment",
740 __PRETTY_FUNCTION__
, name
, up
->sg_str
,
744 up
= pim_upstream_add(sg
, incoming
, flags
, name
);
749 void pim_upstream_ref(struct pim_upstream
*up
, int flags
, const char *name
)
754 zlog_debug("%s(%s): upstream %s ref count %d increment",
755 __PRETTY_FUNCTION__
, name
, up
->sg_str
,
759 struct pim_upstream
*pim_upstream_add(struct prefix_sg
*sg
,
760 struct interface
*incoming
, int flags
,
763 struct pim_upstream
*up
= NULL
;
765 up
= pim_upstream_find(sg
);
767 pim_upstream_ref(up
, flags
, name
);
770 up
= pim_upstream_new(sg
, incoming
, flags
);
773 if (PIM_DEBUG_TRACE
) {
775 char buf
[PREFIX2STR_BUFFER
];
776 prefix2str(&up
->rpf
.rpf_addr
, buf
, sizeof(buf
));
777 zlog_debug("%s(%s): %s, iif %s (%s) found: %d: ref_count: %d",
778 __PRETTY_FUNCTION__
, name
,
779 up
->sg_str
, buf
, up
->rpf
.source_nexthop
.interface
?
780 up
->rpf
.source_nexthop
.interface
->name
: "NIL" ,
781 found
, up
->ref_count
);
783 zlog_debug("%s(%s): (%s) failure to create",
784 __PRETTY_FUNCTION__
, name
,
785 pim_str_sg_dump(sg
));
792 * Passed in up must be the upstream for ch. starch is NULL if no
795 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream
*up
,
796 struct pim_ifchannel
*ch
,
797 struct pim_ifchannel
*starch
)
800 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
803 if (!pim_macro_ch_lost_assert(ch
)
804 && pim_macro_chisin_joins_or_include(ch
))
812 if (PIM_IF_FLAG_TEST_S_G_RPT(starch
->upstream
->flags
))
815 if (!pim_macro_ch_lost_assert(starch
)
816 && pim_macro_chisin_joins_or_include(starch
))
824 Evaluate JoinDesired(S,G):
826 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
829 inherited_olist(S,G) =
830 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
832 JoinDesired(S,G) may be affected by changes in the following:
834 pim_ifp->primary_address
836 ch->ifassert_winner_metric
838 ch->local_ifmembership
840 ch->upstream->rpf.source_nexthop.mrib_metric_preference
841 ch->upstream->rpf.source_nexthop.mrib_route_metric
842 ch->upstream->rpf.source_nexthop.interface
844 See also pim_upstream_update_join_desired() below.
846 int pim_upstream_evaluate_join_desired(struct pim_upstream
*up
)
848 struct interface
*ifp
;
849 struct listnode
*node
;
850 struct pim_ifchannel
*ch
, *starch
;
851 struct pim_upstream
*starup
= up
->parent
;
854 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(pimg
->vrf_id
), node
, ifp
)) {
858 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
861 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
868 ret
+= pim_upstream_evaluate_join_desired_interface(up
, ch
,
870 } /* scan iface channel list */
872 return ret
; /* false */
876 See also pim_upstream_evaluate_join_desired() above.
878 void pim_upstream_update_join_desired(struct pim_upstream
*up
)
880 int was_join_desired
; /* boolean */
881 int is_join_desired
; /* boolean */
883 was_join_desired
= PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up
->flags
);
885 is_join_desired
= pim_upstream_evaluate_join_desired(up
);
887 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up
->flags
);
889 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up
->flags
);
891 /* switched from false to true */
892 if (is_join_desired
&& !was_join_desired
) {
893 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
897 /* switched from true to false */
898 if (!is_join_desired
&& was_join_desired
) {
899 pim_upstream_switch(up
, PIM_UPSTREAM_NOTJOINED
);
905 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
906 Transitions from Joined State
907 RPF'(S,G) GenID changes
909 The upstream (S,G) state machine remains in Joined state. If the
910 Join Timer is set to expire in more than t_override seconds, reset
911 it so that it expires after t_override seconds.
913 void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr
)
915 struct listnode
*up_node
;
916 struct listnode
*up_nextnode
;
917 struct pim_upstream
*up
;
920 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
922 for (ALL_LIST_ELEMENTS(pim_upstream_list
, up_node
, up_nextnode
, up
)) {
924 if (PIM_DEBUG_TRACE
) {
925 char neigh_str
[INET_ADDRSTRLEN
];
926 char rpf_addr_str
[PREFIX_STRLEN
];
927 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
929 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_addr_str
,
930 sizeof(rpf_addr_str
));
932 "%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
933 __PRETTY_FUNCTION__
, neigh_str
, up
->sg_str
,
934 up
->join_state
== PIM_UPSTREAM_JOINED
,
938 /* consider only (S,G) upstream in Joined state */
939 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
942 /* match RPF'(S,G)=neigh_addr */
943 if (up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
!= neigh_addr
.s_addr
)
946 pim_upstream_join_timer_decrease_to_t_override(
947 "RPF'(S,G) GenID change", up
);
952 void pim_upstream_rpf_interface_changed(struct pim_upstream
*up
,
953 struct interface
*old_rpf_ifp
)
955 struct listnode
*chnode
;
956 struct listnode
*chnextnode
;
957 struct pim_ifchannel
*ch
;
959 /* search all ifchannels */
960 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
961 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
963 /* RPF_interface(S) was NOT I */
964 (old_rpf_ifp
== ch
->interface
) &&
965 /* RPF_interface(S) stopped being I */
966 (ch
->upstream
->rpf
.source_nexthop
967 .interface
!= ch
->interface
)) {
968 assert_action_a5(ch
);
970 } /* PIM_IFASSERT_I_AM_LOSER */
972 pim_ifchannel_update_assert_tracking_desired(ch
);
976 void pim_upstream_update_could_assert(struct pim_upstream
*up
)
978 struct listnode
*chnode
;
979 struct listnode
*chnextnode
;
980 struct pim_ifchannel
*ch
;
982 /* scan per-interface (S,G) state */
983 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
984 pim_ifchannel_update_could_assert(ch
);
985 } /* scan iface channel list */
988 void pim_upstream_update_my_assert_metric(struct pim_upstream
*up
)
990 struct listnode
*chnode
;
991 struct listnode
*chnextnode
;
992 struct pim_ifchannel
*ch
;
994 /* scan per-interface (S,G) state */
995 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
996 pim_ifchannel_update_my_assert_metric(ch
);
998 } /* scan iface channel list */
1001 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
)
1003 struct listnode
*chnode
;
1004 struct listnode
*chnextnode
;
1005 struct pim_interface
*pim_ifp
;
1006 struct pim_ifchannel
*ch
;
1008 /* scan per-interface (S,G) state */
1009 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1012 pim_ifp
= ch
->interface
->info
;
1016 pim_ifchannel_update_assert_tracking_desired(ch
);
1018 } /* scan iface channel list */
1021 /* When kat is stopped CouldRegister goes to false so we need to
1022 * transition the (S, G) on FHR to NI state and remove reg tunnel
1024 static void pim_upstream_fhr_kat_expiry(struct pim_upstream
*up
)
1026 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
1029 if (PIM_DEBUG_TRACE
)
1030 zlog_debug("kat expired on %s; clear fhr reg state",
1033 /* stop reg-stop timer */
1034 THREAD_OFF(up
->t_rs_timer
);
1035 /* remove regiface from the OIL if it is there*/
1036 pim_channel_del_oif(up
->channel_oil
, pimg
->regiface
,
1037 PIM_OIF_FLAG_PROTO_PIM
);
1038 /* clear the register state */
1039 up
->reg_state
= PIM_REG_NOINFO
;
1040 PIM_UPSTREAM_FLAG_UNSET_FHR(up
->flags
);
1043 /* When kat is started CouldRegister can go to true. And if it does we
1044 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1046 static void pim_upstream_fhr_kat_start(struct pim_upstream
*up
)
1048 if (pim_upstream_could_register(up
)) {
1049 if (PIM_DEBUG_TRACE
)
1051 "kat started on %s; set fhr reg state to joined",
1054 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1055 if (up
->reg_state
== PIM_REG_NOINFO
)
1056 pim_register_join(up
);
1061 * On an RP, the PMBR value must be cleared when the
1062 * Keepalive Timer expires
1063 * KAT expiry indicates that flow is inactive. If the flow was created or
1064 * maintained by activity now is the time to deref it.
1066 static int pim_upstream_keep_alive_timer(struct thread
*t
)
1068 struct pim_upstream
*up
;
1072 if (I_am_RP(up
->sg
.grp
)) {
1073 pim_br_clear_pmbr(&up
->sg
);
1075 * We need to do more here :)
1076 * But this is the start.
1080 /* source is no longer active - pull the SA from MSDP's cache */
1081 pim_msdp_sa_local_del(&up
->sg
);
1083 /* if entry was created because of activity we need to deref it */
1084 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1085 pim_upstream_fhr_kat_expiry(up
);
1086 if (PIM_DEBUG_TRACE
)
1087 zlog_debug("kat expired on %s; remove stream reference",
1089 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up
->flags
);
1090 pim_upstream_del(up
, __PRETTY_FUNCTION__
);
1091 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
1092 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up
->flags
);
1093 pim_upstream_del(up
, __PRETTY_FUNCTION__
);
1099 void pim_upstream_keep_alive_timer_start(struct pim_upstream
*up
, uint32_t time
)
1101 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1102 if (PIM_DEBUG_TRACE
)
1103 zlog_debug("kat start on %s with no stream reference",
1106 THREAD_OFF(up
->t_ka_timer
);
1107 thread_add_timer(master
, pim_upstream_keep_alive_timer
, up
, time
,
1110 /* any time keepalive is started against a SG we will have to
1111 * re-evaluate our active source database */
1112 pim_msdp_sa_local_update(up
);
1115 /* MSDP on RP needs to know if a source is registerable to this RP */
1116 static int pim_upstream_msdp_reg_timer(struct thread
*t
)
1118 struct pim_upstream
*up
;
1122 /* source is no longer active - pull the SA from MSDP's cache */
1123 pim_msdp_sa_local_del(&up
->sg
);
1126 void pim_upstream_msdp_reg_timer_start(struct pim_upstream
*up
)
1128 THREAD_OFF(up
->t_msdp_reg_timer
);
1129 thread_add_timer(master
, pim_upstream_msdp_reg_timer
, up
,
1130 PIM_MSDP_REG_RXED_PERIOD
, &up
->t_msdp_reg_timer
);
1132 pim_msdp_sa_local_update(up
);
1136 * 4.2.1 Last-Hop Switchover to the SPT
1138 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1139 * RP. Once traffic from sources to joined groups arrives at a last-hop
1140 * router, it has the option of switching to receive the traffic on a
1141 * shortest path tree (SPT).
1143 * The decision for a router to switch to the SPT is controlled as
1147 * CheckSwitchToSpt(S,G) {
1148 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1149 * (+) pim_include(S,G) != NULL )
1150 * AND SwitchToSptDesired(S,G) ) {
1151 * # Note: Restarting the KAT will result in the SPT switch
1152 * set KeepaliveTimer(S,G) to Keepalive_Period
1156 * SwitchToSptDesired(S,G) is a policy function that is implementation
1157 * defined. An "infinite threshold" policy can be implemented by making
1158 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1159 * first packet" policy can be implemented by making
1160 * SwitchToSptDesired(S,G) return true once a single packet has been
1161 * received for the source and group.
1163 int pim_upstream_switch_to_spt_desired(struct prefix_sg
*sg
)
1165 if (I_am_RP(sg
->grp
))
1171 int pim_upstream_is_sg_rpt(struct pim_upstream
*up
)
1173 struct listnode
*chnode
;
1174 struct pim_ifchannel
*ch
;
1176 for (ALL_LIST_ELEMENTS_RO(up
->ifchannels
, chnode
, ch
)) {
1177 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1184 * After receiving a packet set SPTbit:
1186 * Update_SPTbit(S,G,iif) {
1187 * if ( iif == RPF_interface(S)
1188 * AND JoinDesired(S,G) == TRUE
1189 * AND ( DirectlyConnected(S) == TRUE
1190 * OR RPF_interface(S) != RPF_interface(RP(G))
1191 * OR inherited_olist(S,G,rpt) == NULL
1192 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1193 * ( RPF'(S,G) != NULL ) )
1194 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1195 * Set SPTbit(S,G) to TRUE
1199 void pim_upstream_set_sptbit(struct pim_upstream
*up
,
1200 struct interface
*incoming
)
1202 struct pim_upstream
*starup
= up
->parent
;
1204 // iif == RPF_interfvace(S)
1205 if (up
->rpf
.source_nexthop
.interface
!= incoming
) {
1206 if (PIM_DEBUG_TRACE
)
1208 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1209 __PRETTY_FUNCTION__
, incoming
->name
,
1210 up
->rpf
.source_nexthop
.interface
->name
);
1214 // AND JoinDesired(S,G) == TRUE
1217 // DirectlyConnected(S) == TRUE
1218 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1220 if (PIM_DEBUG_TRACE
)
1221 zlog_debug("%s: %s is directly connected to the source",
1222 __PRETTY_FUNCTION__
, up
->sg_str
);
1223 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1227 // OR RPF_interface(S) != RPF_interface(RP(G))
1229 || up
->rpf
.source_nexthop
1230 .interface
!= starup
->rpf
.source_nexthop
.interface
) {
1231 if (PIM_DEBUG_TRACE
)
1233 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1234 __PRETTY_FUNCTION__
, up
->sg_str
);
1235 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1239 // OR inherited_olist(S,G,rpt) == NULL
1240 if (pim_upstream_is_sg_rpt(up
)
1241 && pim_upstream_empty_inherited_olist(up
)) {
1242 if (PIM_DEBUG_TRACE
)
1243 zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
1244 __PRETTY_FUNCTION__
, up
->sg_str
);
1245 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1249 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1250 // ( RPF'(S,G) != NULL ) )
1251 if (up
->parent
&& pim_rpf_is_same(&up
->rpf
, &up
->parent
->rpf
)) {
1252 if (PIM_DEBUG_TRACE
)
1253 zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
1254 __PRETTY_FUNCTION__
, up
->sg_str
);
1255 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1262 const char *pim_upstream_state2str(enum pim_upstream_state join_state
)
1264 switch (join_state
) {
1265 case PIM_UPSTREAM_NOTJOINED
:
1268 case PIM_UPSTREAM_JOINED
:
1275 const char *pim_reg_state2str(enum pim_reg_state reg_state
, char *state_str
)
1277 switch (reg_state
) {
1278 case PIM_REG_NOINFO
:
1279 strcpy(state_str
, "RegNoInfo");
1282 strcpy(state_str
, "RegJoined");
1284 case PIM_REG_JOIN_PENDING
:
1285 strcpy(state_str
, "RegJoinPend");
1288 strcpy(state_str
, "RegPrune");
1291 strcpy(state_str
, "RegUnknown");
1296 static int pim_upstream_register_stop_timer(struct thread
*t
)
1298 struct pim_interface
*pim_ifp
;
1299 struct pim_upstream
*up
;
1300 struct pim_rpf
*rpg
;
1304 if (PIM_DEBUG_TRACE
) {
1305 char state_str
[PIM_REG_STATE_STR_LEN
];
1306 zlog_debug("%s: (S,G)=%s upstream register stop timer %s",
1307 __PRETTY_FUNCTION__
, up
->sg_str
,
1308 pim_reg_state2str(up
->reg_state
, state_str
));
1311 switch (up
->reg_state
) {
1312 case PIM_REG_JOIN_PENDING
:
1313 up
->reg_state
= PIM_REG_JOIN
;
1314 pim_channel_add_oif(up
->channel_oil
, pimg
->regiface
,
1315 PIM_OIF_FLAG_PROTO_PIM
);
1320 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1322 if (PIM_DEBUG_TRACE
)
1324 "%s: Interface: %s is not configured for pim",
1325 __PRETTY_FUNCTION__
,
1326 up
->rpf
.source_nexthop
.interface
->name
);
1329 up
->reg_state
= PIM_REG_JOIN_PENDING
;
1330 pim_upstream_start_register_stop_timer(up
, 1);
1332 if (((up
->channel_oil
->cc
.lastused
/ 100)
1333 > PIM_KEEPALIVE_PERIOD
)
1334 && (I_am_RP(up
->sg
.grp
))) {
1335 if (PIM_DEBUG_TRACE
)
1337 "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
1338 __PRETTY_FUNCTION__
);
1341 rpg
= RP(pimg
, up
->sg
.grp
);
1342 memset(&ip_hdr
, 0, sizeof(struct ip
));
1343 ip_hdr
.ip_p
= PIM_IP_PROTO_PIM
;
1346 ip_hdr
.ip_src
= up
->sg
.src
;
1347 ip_hdr
.ip_dst
= up
->sg
.grp
;
1348 ip_hdr
.ip_len
= htons(20);
1349 // checksum is broken
1350 pim_register_send((uint8_t *)&ip_hdr
, sizeof(struct ip
),
1351 pim_ifp
->primary_address
, rpg
, 1, up
);
1360 void pim_upstream_start_register_stop_timer(struct pim_upstream
*up
,
1365 THREAD_TIMER_OFF(up
->t_rs_timer
);
1367 if (!null_register
) {
1368 uint32_t lower
= (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1369 uint32_t upper
= (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1370 time
= lower
+ (random() % (upper
- lower
+ 1))
1371 - PIM_REGISTER_PROBE_PERIOD
;
1373 time
= PIM_REGISTER_PROBE_PERIOD
;
1375 if (PIM_DEBUG_TRACE
) {
1377 "%s: (S,G)=%s Starting upstream register stop timer %d",
1378 __PRETTY_FUNCTION__
, up
->sg_str
, time
);
1380 thread_add_timer(master
, pim_upstream_register_stop_timer
, up
, time
,
1384 int pim_upstream_inherited_olist_decide(struct pim_upstream
*up
)
1386 struct interface
*ifp
;
1387 struct pim_interface
*pim_ifp
= NULL
;
1388 struct pim_ifchannel
*ch
, *starch
;
1389 struct listnode
*node
;
1390 struct pim_upstream
*starup
= up
->parent
;
1391 int output_intf
= 0;
1393 if (up
->rpf
.source_nexthop
.interface
)
1394 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1396 if (PIM_DEBUG_TRACE
)
1397 zlog_debug("%s: up %s RPF is not present",
1398 __PRETTY_FUNCTION__
, up
->sg_str
);
1400 if (pim_ifp
&& !up
->channel_oil
)
1402 pim_channel_oil_add(&up
->sg
, pim_ifp
->mroute_vif_index
);
1404 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(pimg
->vrf_id
), node
, ifp
)) {
1408 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1411 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
1418 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1420 int flag
= PIM_OIF_FLAG_PROTO_PIM
;
1423 flag
= PIM_OIF_FLAG_PROTO_STAR
;
1425 pim_channel_add_oif(up
->channel_oil
, ifp
, flag
);
1434 * For a given upstream, determine the inherited_olist
1437 * inherited_olist(S,G,rpt) =
1438 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1439 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1440 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1442 * inherited_olist(S,G) =
1443 * inherited_olist(S,G,rpt) (+)
1444 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1446 * return 1 if there are any output interfaces
1447 * return 0 if there are not any output interfaces
1449 int pim_upstream_inherited_olist(struct pim_upstream
*up
)
1451 int output_intf
= pim_upstream_inherited_olist_decide(up
);
1454 * If we have output_intf switch state to Join and work like normal
1455 * If we don't have an output_intf that means we are probably a
1456 * switch on a stick so turn on forwarding to just accept the
1457 * incoming packets so we don't bother the other stuff!
1460 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
1467 int pim_upstream_empty_inherited_olist(struct pim_upstream
*up
)
1469 return pim_channel_oil_empty(up
->channel_oil
);
1473 * When we have a new neighbor,
1474 * find upstreams that don't have their rpf_addr
1475 * set and see if the new neighbor allows
1476 * the join to be sent
1478 void pim_upstream_find_new_rpf(void)
1480 struct listnode
*up_node
;
1481 struct listnode
*up_nextnode
;
1482 struct pim_upstream
*up
;
1485 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1487 for (ALL_LIST_ELEMENTS(pim_upstream_list
, up_node
, up_nextnode
, up
)) {
1488 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
1489 if (PIM_DEBUG_TRACE
)
1491 "Upstream %s without a path to send join, checking",
1493 pim_rpf_update(up
, NULL
, 1);
1498 static unsigned int pim_upstream_hash_key(void *arg
)
1500 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1502 return jhash_2words(up
->sg
.src
.s_addr
, up
->sg
.grp
.s_addr
, 0);
1505 void pim_upstream_terminate(void)
1507 if (pim_upstream_list
)
1508 list_delete(pim_upstream_list
);
1509 pim_upstream_list
= NULL
;
1511 if (pim_upstream_hash
)
1512 hash_free(pim_upstream_hash
);
1513 pim_upstream_hash
= NULL
;
1516 static int pim_upstream_equal(const void *arg1
, const void *arg2
)
1518 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
1519 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
1521 if ((up1
->sg
.grp
.s_addr
== up2
->sg
.grp
.s_addr
)
1522 && (up1
->sg
.src
.s_addr
== up2
->sg
.src
.s_addr
))
1528 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1529 * the cases where kat has to be restarted on rxing traffic -
1531 * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
1532 * set KeepaliveTimer(S,G) to Keepalive_Period
1533 * # Note: a register state transition or UpstreamJPState(S,G)
1534 * # transition may happen as a result of restarting
1535 * # KeepaliveTimer, and must be dealt with here.
1537 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1538 * inherited_olist(S,G) != NULL ) {
1539 * set KeepaliveTimer(S,G) to Keepalive_Period
1542 static bool pim_upstream_kat_start_ok(struct pim_upstream
*up
)
1544 /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
1545 * so we will skip that here */
1546 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1551 if ((up
->join_state
== PIM_UPSTREAM_JOINED
)
1552 && !pim_upstream_empty_inherited_olist(up
)) {
1553 /* XXX: I have added this RP check just for 3.2 and it's a
1555 * what rfc-4601 says. Till now we were only running KAT on FHR
1557 * there is some angst around making the change to run it all
1559 * maintain the (S, G) state. This is tracked via CM-13601 and
1561 * removed to handle spt turn-arounds correctly in a 3-tier clos
1563 if (I_am_RP(up
->sg
.grp
))
1571 * Code to check and see if we've received packets on a S,G mroute
1572 * and if so to set the SPT bit appropriately
1574 static void pim_upstream_sg_running(void *arg
)
1576 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1578 // No packet can have arrived here if this is the case
1579 if (!up
->channel_oil
|| !up
->channel_oil
->installed
) {
1580 if (PIM_DEBUG_TRACE
)
1581 zlog_debug("%s: %s is not installed in mroute",
1582 __PRETTY_FUNCTION__
, up
->sg_str
);
1587 * This is a bit of a hack
1588 * We've noted that we should rescan but
1589 * we've missed the window for doing so in
1590 * pim_zebra.c for some reason. I am
1591 * only doing this at this point in time
1592 * to get us up and working for the moment
1594 if (up
->channel_oil
->oil_inherited_rescan
) {
1595 if (PIM_DEBUG_TRACE
)
1597 "%s: Handling unscanned inherited_olist for %s",
1598 __PRETTY_FUNCTION__
, up
->sg_str
);
1599 pim_upstream_inherited_olist_decide(up
);
1600 up
->channel_oil
->oil_inherited_rescan
= 0;
1602 pim_mroute_update_counters(up
->channel_oil
);
1604 // Have we seen packets?
1605 if ((up
->channel_oil
->cc
.oldpktcnt
>= up
->channel_oil
->cc
.pktcnt
)
1606 && (up
->channel_oil
->cc
.lastused
/ 100 > 30)) {
1607 if (PIM_DEBUG_TRACE
) {
1609 "%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
1610 __PRETTY_FUNCTION__
, up
->sg_str
,
1611 up
->channel_oil
->cc
.oldpktcnt
,
1612 up
->channel_oil
->cc
.pktcnt
,
1613 up
->channel_oil
->cc
.lastused
/ 100);
1618 if (pim_upstream_kat_start_ok(up
)) {
1619 /* Add a source reference to the stream if
1620 * one doesn't already exist */
1621 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1622 if (PIM_DEBUG_TRACE
)
1624 "source reference created on kat restart %s",
1627 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM
,
1628 __PRETTY_FUNCTION__
);
1629 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
1630 pim_upstream_fhr_kat_start(up
);
1632 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
1633 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
))
1634 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
1636 if (up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
) {
1637 pim_upstream_set_sptbit(up
, up
->rpf
.source_nexthop
.interface
);
1642 void pim_upstream_add_lhr_star_pimreg(void)
1644 struct pim_upstream
*up
;
1645 struct listnode
*node
;
1647 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, node
, up
)) {
1648 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
1651 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
))
1654 pim_channel_add_oif(up
->channel_oil
, pimg
->regiface
,
1655 PIM_OIF_FLAG_PROTO_IGMP
);
1659 void pim_upstream_spt_prefix_list_update(struct prefix_list
*pl
)
1661 const char *pname
= prefix_list_name(pl
);
1663 if (pimg
->spt
.plist
&& strcmp(pimg
->spt
.plist
, pname
) == 0) {
1664 pim_upstream_remove_lhr_star_pimreg(pname
);
1669 * nlist -> The new prefix list
1671 * Per Group Application of pimreg to the OIL
1672 * If the prefix list tells us DENY then
1673 * we need to Switchover to SPT immediate
1674 * so add the pimreg.
1675 * If the prefix list tells us to ACCEPT than
1676 * we need to Never do the SPT so remove
1680 void pim_upstream_remove_lhr_star_pimreg(const char *nlist
)
1682 struct pim_upstream
*up
;
1683 struct listnode
*node
;
1684 struct prefix_list
*np
;
1686 enum prefix_list_type apply_new
;
1688 np
= prefix_list_lookup(AFI_IP
, nlist
);
1691 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1693 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, node
, up
)) {
1694 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
1697 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
))
1701 pim_channel_del_oif(up
->channel_oil
, pimg
->regiface
,
1702 PIM_OIF_FLAG_PROTO_IGMP
);
1705 g
.u
.prefix4
= up
->sg
.grp
;
1706 apply_new
= prefix_list_apply(np
, &g
);
1707 if (apply_new
== PREFIX_DENY
)
1708 pim_channel_add_oif(up
->channel_oil
, pimg
->regiface
,
1709 PIM_OIF_FLAG_PROTO_IGMP
);
1711 pim_channel_del_oif(up
->channel_oil
, pimg
->regiface
,
1712 PIM_OIF_FLAG_PROTO_IGMP
);
1716 void pim_upstream_init(void)
1718 pim_upstream_sg_wheel
=
1719 wheel_init(master
, 31000, 100, pim_upstream_hash_key
,
1720 pim_upstream_sg_running
);
1721 pim_upstream_hash
= hash_create_size(8192, pim_upstream_hash_key
,
1722 pim_upstream_equal
, NULL
);
1724 pim_upstream_list
= list_new();
1725 pim_upstream_list
->del
= (void (*)(void *))pim_upstream_free
;
1726 pim_upstream_list
->cmp
= pim_upstream_compare
;