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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
23 #include "zebra/rib.h"
40 #include "pim_iface.h"
42 #include "pim_zlookup.h"
43 #include "pim_upstream.h"
44 #include "pim_ifchannel.h"
45 #include "pim_neighbor.h"
47 #include "pim_zebra.h"
49 #include "pim_macro.h"
52 #include "pim_register.h"
54 #include "pim_jp_agg.h"
58 struct hash
*pim_upstream_hash
= NULL
;
59 struct list
*pim_upstream_list
= NULL
;
60 struct timer_wheel
*pim_upstream_sg_wheel
= NULL
;
62 static void join_timer_stop(struct pim_upstream
*up
);
64 pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
);
67 * A (*,G) or a (*,*) is going away
68 * remove the parent pointer from
69 * those pointing at us
71 static void pim_upstream_remove_children(struct pim_upstream
*up
)
73 struct pim_upstream
*child
;
78 while (!list_isempty(up
->sources
)) {
79 child
= listnode_head(up
->sources
);
80 listnode_delete(up
->sources
, child
);
81 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child
->flags
)) {
82 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child
->flags
);
83 child
= pim_upstream_del(child
, __PRETTY_FUNCTION__
);
88 list_delete(up
->sources
);
93 * A (*,G) or a (*,*) is being created
94 * Find the children that would point
97 static void pim_upstream_find_new_children(struct pim_upstream
*up
)
99 struct pim_upstream
*child
;
100 struct listnode
*ch_node
;
102 if ((up
->sg
.src
.s_addr
!= INADDR_ANY
)
103 && (up
->sg
.grp
.s_addr
!= INADDR_ANY
))
106 if ((up
->sg
.src
.s_addr
== INADDR_ANY
)
107 && (up
->sg
.grp
.s_addr
== INADDR_ANY
))
110 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, ch_node
, child
)) {
111 if ((up
->sg
.grp
.s_addr
!= INADDR_ANY
)
112 && (child
->sg
.grp
.s_addr
== up
->sg
.grp
.s_addr
)
115 listnode_add_sort(up
->sources
, child
);
121 * If we have a (*,*) || (S,*) there is no parent
122 * If we have a (S,G), find the (*,G)
123 * If we have a (*,G), find the (*,*)
125 static struct pim_upstream
*pim_upstream_find_parent(struct pim_upstream
*child
)
127 struct prefix_sg any
= child
->sg
;
128 struct pim_upstream
*up
= NULL
;
131 if ((child
->sg
.src
.s_addr
!= INADDR_ANY
)
132 && (child
->sg
.grp
.s_addr
!= INADDR_ANY
)) {
133 any
.src
.s_addr
= INADDR_ANY
;
134 up
= pim_upstream_find(&any
);
137 listnode_add(up
->sources
, child
);
145 void pim_upstream_free(struct pim_upstream
*up
)
147 XFREE(MTYPE_PIM_UPSTREAM
, up
);
151 static void upstream_channel_oil_detach(struct pim_upstream
*up
)
153 if (up
->channel_oil
) {
154 /* Detaching from channel_oil, channel_oil may exist post del,
155 but upstream would not keep reference of it
157 pim_channel_oil_del(up
->channel_oil
);
158 up
->channel_oil
= NULL
;
162 struct pim_upstream
*pim_upstream_del(struct pim_upstream
*up
, const char *name
)
164 bool notify_msdp
= false;
169 "%s(%s): Delete %s ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
170 __PRETTY_FUNCTION__
, name
, up
->sg_str
, up
->ref_count
,
171 up
->flags
, up
->channel_oil
->oil_ref_count
);
175 if (up
->ref_count
>= 1)
178 THREAD_OFF(up
->t_ka_timer
);
179 THREAD_OFF(up
->t_rs_timer
);
180 THREAD_OFF(up
->t_msdp_reg_timer
);
182 if (up
->join_state
== PIM_UPSTREAM_JOINED
) {
183 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0);
185 if (up
->sg
.src
.s_addr
== INADDR_ANY
) {
186 /* if a (*, G) entry in the joined state is being
188 * need to notify MSDP */
194 pim_jp_agg_upstream_verification(up
, false);
195 up
->rpf
.source_nexthop
.interface
= NULL
;
197 if (up
->sg
.src
.s_addr
!= INADDR_ANY
) {
198 wheel_remove_item(pim_upstream_sg_wheel
, up
);
202 pim_upstream_remove_children(up
);
204 list_delete(up
->sources
);
206 pim_mroute_del(up
->channel_oil
, __PRETTY_FUNCTION__
);
207 upstream_channel_oil_detach(up
);
209 list_delete(up
->ifchannels
);
210 up
->ifchannels
= NULL
;
213 notice that listnode_delete() can't be moved
214 into pim_upstream_free() because the later is
215 called by list_delete_all_node()
217 if (up
->parent
&& up
->parent
->sources
)
218 listnode_delete(up
->parent
->sources
, up
);
221 listnode_delete(pim_upstream_list
, up
);
222 hash_release(pim_upstream_hash
, up
);
225 pim_msdp_up_del(&up
->sg
);
228 /* Deregister addr with Zebra NHT */
229 nht_p
.family
= AF_INET
;
230 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
231 nht_p
.u
.prefix4
= up
->upstream_addr
;
232 if (PIM_DEBUG_TRACE
) {
233 char buf
[PREFIX2STR_BUFFER
];
234 prefix2str(&nht_p
, buf
, sizeof(buf
));
235 zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT",
236 __PRETTY_FUNCTION__
, up
->sg_str
, buf
);
238 pim_delete_tracked_nexthop(&nht_p
, up
, NULL
);
240 pim_upstream_free(up
);
245 void pim_upstream_send_join(struct pim_upstream
*up
)
247 if (PIM_DEBUG_TRACE
) {
248 char rpf_str
[PREFIX_STRLEN
];
249 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
251 zlog_debug("%s: RPF'%s=%s(%s) for Interface %s",
252 __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
,
253 pim_upstream_state2str(up
->join_state
),
254 up
->rpf
.source_nexthop
.interface
->name
);
255 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
256 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
257 __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
);
262 /* send Join(S,G) to the current upstream neighbor */
263 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 1 /* join */);
266 static int on_join_timer(struct thread
*t
)
268 struct pim_upstream
*up
;
272 up
->t_join_timer
= NULL
;
275 * In the case of a HFR we will not ahve anyone to send this to.
277 if (PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
281 * Don't send the join if the outgoing interface is a loopback
282 * But since this might change leave the join timer running
284 if (up
->rpf
.source_nexthop
285 .interface
&& !if_is_loopback(up
->rpf
.source_nexthop
.interface
))
286 pim_upstream_send_join(up
);
288 join_timer_start(up
);
293 static void join_timer_stop(struct pim_upstream
*up
)
295 struct pim_neighbor
*nbr
;
297 THREAD_OFF(up
->t_join_timer
);
299 nbr
= pim_neighbor_find(up
->rpf
.source_nexthop
.interface
,
300 up
->rpf
.rpf_addr
.u
.prefix4
);
303 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
);
305 pim_jp_agg_upstream_verification(up
, false);
308 void join_timer_start(struct pim_upstream
*up
)
310 struct pim_neighbor
*nbr
= NULL
;
312 if (up
->rpf
.source_nexthop
.interface
) {
313 nbr
= pim_neighbor_find(up
->rpf
.source_nexthop
.interface
,
314 up
->rpf
.rpf_addr
.u
.prefix4
);
316 if (PIM_DEBUG_PIM_EVENTS
) {
318 "%s: starting %d sec timer for upstream (S,G)=%s",
319 __PRETTY_FUNCTION__
, qpim_t_periodic
,
325 pim_jp_agg_add_group(nbr
->upstream_jp_agg
, up
, 1);
327 THREAD_OFF(up
->t_join_timer
);
328 THREAD_TIMER_ON(master
, up
->t_join_timer
, on_join_timer
, up
,
331 pim_jp_agg_upstream_verification(up
, true);
335 * This is only called when we are switching the upstream
336 * J/P from one neighbor to another
338 * As such we need to remove from the old list and
339 * add to the new list.
341 void pim_upstream_join_timer_restart(struct pim_upstream
*up
,
344 // THREAD_OFF(up->t_join_timer);
345 join_timer_start(up
);
348 static void pim_upstream_join_timer_restart_msec(struct pim_upstream
*up
,
351 if (PIM_DEBUG_PIM_EVENTS
) {
352 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
353 __PRETTY_FUNCTION__
, interval_msec
, up
->sg_str
);
356 THREAD_OFF(up
->t_join_timer
);
357 THREAD_TIMER_MSEC_ON(master
, up
->t_join_timer
, on_join_timer
, up
,
361 void pim_upstream_join_suppress(struct pim_upstream
*up
,
362 struct in_addr rpf_addr
, int holdtime
)
364 long t_joinsuppress_msec
;
365 long join_timer_remain_msec
;
367 t_joinsuppress_msec
=
368 MIN(pim_if_t_suppressed_msec(up
->rpf
.source_nexthop
.interface
),
371 join_timer_remain_msec
= pim_time_timer_remain_msec(up
->t_join_timer
);
373 if (PIM_DEBUG_TRACE
) {
374 char rpf_str
[INET_ADDRSTRLEN
];
375 pim_inet4_dump("<rpf?>", rpf_addr
, rpf_str
, sizeof(rpf_str
));
377 "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
378 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
,
379 join_timer_remain_msec
, t_joinsuppress_msec
);
382 if (join_timer_remain_msec
< t_joinsuppress_msec
) {
383 if (PIM_DEBUG_TRACE
) {
385 "%s %s: suppressing Join(S,G)=%s for %ld msec",
386 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
,
387 t_joinsuppress_msec
);
390 pim_upstream_join_timer_restart_msec(up
, t_joinsuppress_msec
);
394 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label
,
395 struct pim_upstream
*up
)
397 long join_timer_remain_msec
;
400 join_timer_remain_msec
= pim_time_timer_remain_msec(up
->t_join_timer
);
402 pim_if_t_override_msec(up
->rpf
.source_nexthop
.interface
);
404 if (PIM_DEBUG_TRACE
) {
405 char rpf_str
[INET_ADDRSTRLEN
];
406 pim_inet4_dump("<rpf?>", up
->rpf
.rpf_addr
.u
.prefix4
, rpf_str
,
409 "%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
410 debug_label
, up
->sg_str
, rpf_str
,
411 join_timer_remain_msec
, t_override_msec
);
414 if (join_timer_remain_msec
> t_override_msec
) {
415 if (PIM_DEBUG_TRACE
) {
417 "%s: decreasing (S,G)=%s join timer to t_override=%d msec",
418 debug_label
, up
->sg_str
, t_override_msec
);
421 pim_upstream_join_timer_restart_msec(up
, t_override_msec
);
425 static void forward_on(struct pim_upstream
*up
)
427 struct listnode
*chnode
;
428 struct listnode
*chnextnode
;
429 struct pim_ifchannel
*ch
= NULL
;
431 /* scan (S,G) state */
432 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
433 if (pim_macro_chisin_oiflist(ch
))
434 pim_forward_start(ch
);
436 } /* scan iface channel list */
439 static void forward_off(struct pim_upstream
*up
)
441 struct listnode
*chnode
;
442 struct listnode
*chnextnode
;
443 struct pim_ifchannel
*ch
;
445 /* scan per-interface (S,G) state */
446 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
448 pim_forward_stop(ch
);
450 } /* scan iface channel list */
453 static int pim_upstream_could_register(struct pim_upstream
*up
)
455 struct pim_interface
*pim_ifp
= NULL
;
457 if (up
->rpf
.source_nexthop
.interface
)
458 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
461 zlog_debug("%s: up %s RPF is not present",
462 __PRETTY_FUNCTION__
, up
->sg_str
);
465 if (pim_ifp
&& PIM_I_am_DR(pim_ifp
)
466 && pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
473 /* Source registration is supressed for SSM groups. When the SSM range changes
474 * we re-revaluate register setup for existing upstream entries */
475 void pim_upstream_register_reevaluate(void)
477 struct listnode
*upnode
;
478 struct pim_upstream
*up
;
480 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, upnode
, up
)) {
481 /* If FHR is set CouldRegister is True. Also check if the flow
482 * is actually active; if it is not kat setup will trigger
484 * registration whenever the flow becomes active. */
485 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) || !up
->t_ka_timer
)
488 if (pim_is_grp_ssm(up
->sg
.grp
)) {
489 /* clear the register state for SSM groups */
490 if (up
->reg_state
!= PIM_REG_NOINFO
) {
491 if (PIM_DEBUG_PIM_EVENTS
)
493 "Clear register for %s as G is now SSM",
495 /* remove regiface from the OIL if it is there*/
496 pim_channel_del_oif(up
->channel_oil
,
498 PIM_OIF_FLAG_PROTO_PIM
);
499 up
->reg_state
= PIM_REG_NOINFO
;
502 /* register ASM sources with the RP */
503 if (up
->reg_state
== PIM_REG_NOINFO
) {
504 if (PIM_DEBUG_PIM_EVENTS
)
506 "Register %s as G is now ASM",
508 pim_channel_add_oif(up
->channel_oil
,
510 PIM_OIF_FLAG_PROTO_PIM
);
511 up
->reg_state
= PIM_REG_JOIN
;
517 void pim_upstream_switch(struct pim_upstream
*up
,
518 enum pim_upstream_state new_state
)
520 enum pim_upstream_state old_state
= up
->join_state
;
522 if (PIM_DEBUG_PIM_EVENTS
) {
523 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
524 __PRETTY_FUNCTION__
, up
->sg_str
,
525 pim_upstream_state2str(up
->join_state
),
526 pim_upstream_state2str(new_state
));
529 up
->join_state
= new_state
;
530 if (old_state
!= new_state
)
531 up
->state_transition
= pim_time_monotonic_sec();
533 pim_upstream_update_assert_tracking_desired(up
);
535 if (new_state
== PIM_UPSTREAM_JOINED
) {
536 if (old_state
!= PIM_UPSTREAM_JOINED
) {
537 int old_fhr
= PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
);
539 pim_msdp_up_join_state_changed(up
);
540 if (pim_upstream_could_register(up
)) {
541 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
543 && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(
545 pim_upstream_keep_alive_timer_start(
546 up
, qpim_keep_alive_time
);
547 pim_register_join(up
);
550 pim_upstream_send_join(up
);
551 join_timer_start(up
);
559 if (old_state
== PIM_UPSTREAM_JOINED
)
560 pim_msdp_up_join_state_changed(up
);
562 /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
564 If I am RP for G then send S,G prune to its IIF. */
565 if (pim_upstream_is_sg_rpt(up
) && up
->parent
566 && !I_am_RP(up
->sg
.grp
)) {
567 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
569 "%s: *,G IIF %s S,G IIF %s ",
571 up
->parent
->rpf
.source_nexthop
573 up
->rpf
.source_nexthop
.interface
->name
);
574 pim_jp_agg_single_upstream_send(&up
->parent
->rpf
,
578 pim_jp_agg_single_upstream_send(&up
->rpf
, up
,
584 int pim_upstream_compare(void *arg1
, void *arg2
)
586 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
587 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
589 if (ntohl(up1
->sg
.grp
.s_addr
) < ntohl(up2
->sg
.grp
.s_addr
))
592 if (ntohl(up1
->sg
.grp
.s_addr
) > ntohl(up2
->sg
.grp
.s_addr
))
595 if (ntohl(up1
->sg
.src
.s_addr
) < ntohl(up2
->sg
.src
.s_addr
))
598 if (ntohl(up1
->sg
.src
.s_addr
) > ntohl(up2
->sg
.src
.s_addr
))
604 static struct pim_upstream
*
605 pim_upstream_new(struct prefix_sg
*sg
, struct interface
*incoming
, int flags
)
607 enum pim_rpf_result rpf_result
;
608 struct pim_interface
*pim_ifp
;
609 struct pim_upstream
*up
;
611 up
= XCALLOC(MTYPE_PIM_UPSTREAM
, sizeof(*up
));
613 zlog_err("%s: PIM XCALLOC(%zu) failure", __PRETTY_FUNCTION__
,
619 pim_str_sg_set(sg
, up
->sg_str
);
620 up
= hash_get(pim_upstream_hash
, up
, hash_alloc_intern
);
621 if (!pim_rp_set_upstream_addr(&up
->upstream_addr
, sg
->src
, sg
->grp
)) {
623 zlog_debug("%s: Received a (*,G) with no RP configured",
624 __PRETTY_FUNCTION__
);
626 hash_release(pim_upstream_hash
, up
);
627 XFREE(MTYPE_PIM_UPSTREAM
, up
);
631 up
->parent
= pim_upstream_find_parent(up
);
632 if (up
->sg
.src
.s_addr
== INADDR_ANY
) {
633 up
->sources
= list_new();
634 up
->sources
->cmp
= pim_upstream_compare
;
638 pim_upstream_find_new_children(up
);
641 up
->t_join_timer
= NULL
;
642 up
->t_ka_timer
= NULL
;
643 up
->t_rs_timer
= NULL
;
644 up
->t_msdp_reg_timer
= NULL
;
645 up
->join_state
= PIM_UPSTREAM_NOTJOINED
;
646 up
->reg_state
= PIM_REG_NOINFO
;
647 up
->state_transition
= pim_time_monotonic_sec();
648 up
->channel_oil
= NULL
;
649 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
651 up
->rpf
.source_nexthop
.interface
= NULL
;
652 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.family
= AF_INET
;
653 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
.s_addr
=
655 up
->rpf
.source_nexthop
.mrib_metric_preference
=
656 qpim_infinite_assert_metric
.metric_preference
;
657 up
->rpf
.source_nexthop
.mrib_route_metric
=
658 qpim_infinite_assert_metric
.route_metric
;
659 up
->rpf
.rpf_addr
.family
= AF_INET
;
660 up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
= PIM_NET_INADDR_ANY
;
662 up
->ifchannels
= list_new();
663 up
->ifchannels
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
665 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
666 wheel_add_item(pim_upstream_sg_wheel
, up
);
668 rpf_result
= pim_rpf_update(up
, NULL
, 1);
669 if (rpf_result
== PIM_RPF_FAILURE
) {
674 "%s: Attempting to create upstream(%s), Unable to RPF for source",
675 __PRETTY_FUNCTION__
, up
->sg_str
);
677 nht_p
.family
= AF_INET
;
678 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
679 nht_p
.u
.prefix4
= up
->upstream_addr
;
680 pim_delete_tracked_nexthop(&nht_p
, up
, NULL
);
683 listnode_delete(up
->parent
->sources
, up
);
687 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
688 wheel_remove_item(pim_upstream_sg_wheel
, up
);
690 pim_upstream_remove_children(up
);
692 list_delete(up
->sources
);
694 hash_release(pim_upstream_hash
, up
);
695 XFREE(MTYPE_PIM_UPSTREAM
, up
);
699 if (up
->rpf
.source_nexthop
.interface
) {
700 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
702 up
->channel_oil
= pim_channel_oil_add(
703 &up
->sg
, pim_ifp
->mroute_vif_index
);
705 listnode_add_sort(pim_upstream_list
, up
);
707 if (PIM_DEBUG_TRACE
) {
709 "%s: Created Upstream %s upstream_addr %s ref count %d increment",
710 __PRETTY_FUNCTION__
, up
->sg_str
,
711 inet_ntoa(up
->upstream_addr
), up
->ref_count
);
717 struct pim_upstream
*pim_upstream_find(struct prefix_sg
*sg
)
719 struct pim_upstream lookup
;
720 struct pim_upstream
*up
= NULL
;
723 up
= hash_lookup(pim_upstream_hash
, &lookup
);
727 struct pim_upstream
*pim_upstream_find_or_add(struct prefix_sg
*sg
,
728 struct interface
*incoming
,
729 int flags
, const char *name
)
731 struct pim_upstream
*up
;
733 up
= pim_upstream_find(sg
);
736 if (!(up
->flags
& flags
)) {
741 "%s(%s): upstream %s ref count %d increment",
742 __PRETTY_FUNCTION__
, name
, up
->sg_str
,
746 up
= pim_upstream_add(sg
, incoming
, flags
, name
);
751 void pim_upstream_ref(struct pim_upstream
*up
, int flags
, const char *name
)
756 zlog_debug("%s(%s): upstream %s ref count %d increment",
757 __PRETTY_FUNCTION__
, name
, up
->sg_str
,
761 struct pim_upstream
*pim_upstream_add(struct prefix_sg
*sg
,
762 struct interface
*incoming
, int flags
,
765 struct pim_upstream
*up
= NULL
;
767 up
= pim_upstream_find(sg
);
769 pim_upstream_ref(up
, flags
, name
);
772 up
= pim_upstream_new(sg
, incoming
, flags
);
775 if (PIM_DEBUG_TRACE
) {
777 char buf
[PREFIX2STR_BUFFER
];
778 prefix2str(&up
->rpf
.rpf_addr
, buf
, sizeof(buf
));
779 zlog_debug("%s(%s): %s, iif %s (%s) found: %d: ref_count: %d",
780 __PRETTY_FUNCTION__
, name
,
781 up
->sg_str
, buf
, up
->rpf
.source_nexthop
.interface
?
782 up
->rpf
.source_nexthop
.interface
->name
: "NIL" ,
783 found
, up
->ref_count
);
785 zlog_debug("%s(%s): (%s) failure to create",
786 __PRETTY_FUNCTION__
, name
,
787 pim_str_sg_dump(sg
));
794 * Passed in up must be the upstream for ch. starch is NULL if no
797 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream
*up
,
798 struct pim_ifchannel
*ch
,
799 struct pim_ifchannel
*starch
)
802 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
805 if (!pim_macro_ch_lost_assert(ch
)
806 && pim_macro_chisin_joins_or_include(ch
))
814 if (PIM_IF_FLAG_TEST_S_G_RPT(starch
->upstream
->flags
))
817 if (!pim_macro_ch_lost_assert(starch
)
818 && pim_macro_chisin_joins_or_include(starch
))
826 Evaluate JoinDesired(S,G):
828 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
831 inherited_olist(S,G) =
832 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
834 JoinDesired(S,G) may be affected by changes in the following:
836 pim_ifp->primary_address
838 ch->ifassert_winner_metric
840 ch->local_ifmembership
842 ch->upstream->rpf.source_nexthop.mrib_metric_preference
843 ch->upstream->rpf.source_nexthop.mrib_route_metric
844 ch->upstream->rpf.source_nexthop.interface
846 See also pim_upstream_update_join_desired() below.
848 int pim_upstream_evaluate_join_desired(struct pim_upstream
*up
)
850 struct interface
*ifp
;
851 struct listnode
*node
;
852 struct pim_ifchannel
*ch
, *starch
;
853 struct pim_upstream
*starup
= up
->parent
;
856 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT
), node
, ifp
)) {
860 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
863 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
870 ret
+= pim_upstream_evaluate_join_desired_interface(up
, ch
,
872 } /* scan iface channel list */
874 return ret
; /* false */
878 See also pim_upstream_evaluate_join_desired() above.
880 void pim_upstream_update_join_desired(struct pim_upstream
*up
)
882 int was_join_desired
; /* boolean */
883 int is_join_desired
; /* boolean */
885 was_join_desired
= PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up
->flags
);
887 is_join_desired
= pim_upstream_evaluate_join_desired(up
);
889 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up
->flags
);
891 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up
->flags
);
893 /* switched from false to true */
894 if (is_join_desired
&& !was_join_desired
) {
895 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
899 /* switched from true to false */
900 if (!is_join_desired
&& was_join_desired
) {
901 pim_upstream_switch(up
, PIM_UPSTREAM_NOTJOINED
);
907 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
908 Transitions from Joined State
909 RPF'(S,G) GenID changes
911 The upstream (S,G) state machine remains in Joined state. If the
912 Join Timer is set to expire in more than t_override seconds, reset
913 it so that it expires after t_override seconds.
915 void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr
)
917 struct listnode
*up_node
;
918 struct listnode
*up_nextnode
;
919 struct pim_upstream
*up
;
922 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
924 for (ALL_LIST_ELEMENTS(pim_upstream_list
, up_node
, up_nextnode
, up
)) {
926 if (PIM_DEBUG_TRACE
) {
927 char neigh_str
[INET_ADDRSTRLEN
];
928 char rpf_addr_str
[PREFIX_STRLEN
];
929 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
931 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_addr_str
,
932 sizeof(rpf_addr_str
));
934 "%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
935 __PRETTY_FUNCTION__
, neigh_str
, up
->sg_str
,
936 up
->join_state
== PIM_UPSTREAM_JOINED
,
940 /* consider only (S,G) upstream in Joined state */
941 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
944 /* match RPF'(S,G)=neigh_addr */
945 if (up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
!= neigh_addr
.s_addr
)
948 pim_upstream_join_timer_decrease_to_t_override(
949 "RPF'(S,G) GenID change", up
);
954 void pim_upstream_rpf_interface_changed(struct pim_upstream
*up
,
955 struct interface
*old_rpf_ifp
)
957 struct listnode
*chnode
;
958 struct listnode
*chnextnode
;
959 struct pim_ifchannel
*ch
;
961 /* search all ifchannels */
962 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
963 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
965 /* RPF_interface(S) was NOT I */
966 (old_rpf_ifp
== ch
->interface
) &&
967 /* RPF_interface(S) stopped being I */
968 (ch
->upstream
->rpf
.source_nexthop
969 .interface
!= ch
->interface
)) {
970 assert_action_a5(ch
);
972 } /* PIM_IFASSERT_I_AM_LOSER */
974 pim_ifchannel_update_assert_tracking_desired(ch
);
978 void pim_upstream_update_could_assert(struct pim_upstream
*up
)
980 struct listnode
*chnode
;
981 struct listnode
*chnextnode
;
982 struct pim_ifchannel
*ch
;
984 /* scan per-interface (S,G) state */
985 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
986 pim_ifchannel_update_could_assert(ch
);
987 } /* scan iface channel list */
990 void pim_upstream_update_my_assert_metric(struct pim_upstream
*up
)
992 struct listnode
*chnode
;
993 struct listnode
*chnextnode
;
994 struct pim_ifchannel
*ch
;
996 /* scan per-interface (S,G) state */
997 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
998 pim_ifchannel_update_my_assert_metric(ch
);
1000 } /* scan iface channel list */
1003 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
)
1005 struct listnode
*chnode
;
1006 struct listnode
*chnextnode
;
1007 struct pim_interface
*pim_ifp
;
1008 struct pim_ifchannel
*ch
;
1010 /* scan per-interface (S,G) state */
1011 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1014 pim_ifp
= ch
->interface
->info
;
1018 pim_ifchannel_update_assert_tracking_desired(ch
);
1020 } /* scan iface channel list */
1023 /* When kat is stopped CouldRegister goes to false so we need to
1024 * transition the (S, G) on FHR to NI state and remove reg tunnel
1026 static void pim_upstream_fhr_kat_expiry(struct pim_upstream
*up
)
1028 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
1031 if (PIM_DEBUG_TRACE
)
1032 zlog_debug("kat expired on %s; clear fhr reg state",
1035 /* stop reg-stop timer */
1036 THREAD_OFF(up
->t_rs_timer
);
1037 /* remove regiface from the OIL if it is there*/
1038 pim_channel_del_oif(up
->channel_oil
, pim_regiface
,
1039 PIM_OIF_FLAG_PROTO_PIM
);
1040 /* clear the register state */
1041 up
->reg_state
= PIM_REG_NOINFO
;
1042 PIM_UPSTREAM_FLAG_UNSET_FHR(up
->flags
);
1045 /* When kat is started CouldRegister can go to true. And if it does we
1046 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1048 static void pim_upstream_fhr_kat_start(struct pim_upstream
*up
)
1050 if (pim_upstream_could_register(up
)) {
1051 if (PIM_DEBUG_TRACE
)
1053 "kat started on %s; set fhr reg state to joined",
1056 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1057 if (up
->reg_state
== PIM_REG_NOINFO
)
1058 pim_register_join(up
);
1063 * On an RP, the PMBR value must be cleared when the
1064 * Keepalive Timer expires
1065 * KAT expiry indicates that flow is inactive. If the flow was created or
1066 * maintained by activity now is the time to deref it.
1068 static int pim_upstream_keep_alive_timer(struct thread
*t
)
1070 struct pim_upstream
*up
;
1073 up
->t_ka_timer
= NULL
;
1075 if (I_am_RP(up
->sg
.grp
)) {
1076 pim_br_clear_pmbr(&up
->sg
);
1078 * We need to do more here :)
1079 * But this is the start.
1083 /* source is no longer active - pull the SA from MSDP's cache */
1084 pim_msdp_sa_local_del(&up
->sg
);
1086 /* if entry was created because of activity we need to deref it */
1087 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1088 pim_upstream_fhr_kat_expiry(up
);
1089 if (PIM_DEBUG_TRACE
)
1090 zlog_debug("kat expired on %s; remove stream reference",
1092 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up
->flags
);
1093 pim_upstream_del(up
, __PRETTY_FUNCTION__
);
1094 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
1095 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up
->flags
);
1096 pim_upstream_del(up
, __PRETTY_FUNCTION__
);
1102 void pim_upstream_keep_alive_timer_start(struct pim_upstream
*up
, uint32_t time
)
1104 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1105 if (PIM_DEBUG_TRACE
)
1106 zlog_debug("kat start on %s with no stream reference",
1109 THREAD_OFF(up
->t_ka_timer
);
1110 THREAD_TIMER_ON(master
, up
->t_ka_timer
, pim_upstream_keep_alive_timer
,
1113 /* any time keepalive is started against a SG we will have to
1114 * re-evaluate our active source database */
1115 pim_msdp_sa_local_update(up
);
1118 /* MSDP on RP needs to know if a source is registerable to this RP */
1119 static int pim_upstream_msdp_reg_timer(struct thread
*t
)
1121 struct pim_upstream
*up
;
1124 up
->t_msdp_reg_timer
= NULL
;
1126 /* source is no longer active - pull the SA from MSDP's cache */
1127 pim_msdp_sa_local_del(&up
->sg
);
1130 void pim_upstream_msdp_reg_timer_start(struct pim_upstream
*up
)
1132 THREAD_OFF(up
->t_msdp_reg_timer
);
1133 THREAD_TIMER_ON(master
, up
->t_msdp_reg_timer
,
1134 pim_upstream_msdp_reg_timer
, up
,
1135 PIM_MSDP_REG_RXED_PERIOD
);
1137 pim_msdp_sa_local_update(up
);
1141 * 4.2.1 Last-Hop Switchover to the SPT
1143 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1144 * RP. Once traffic from sources to joined groups arrives at a last-hop
1145 * router, it has the option of switching to receive the traffic on a
1146 * shortest path tree (SPT).
1148 * The decision for a router to switch to the SPT is controlled as
1152 * CheckSwitchToSpt(S,G) {
1153 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1154 * (+) pim_include(S,G) != NULL )
1155 * AND SwitchToSptDesired(S,G) ) {
1156 * # Note: Restarting the KAT will result in the SPT switch
1157 * set KeepaliveTimer(S,G) to Keepalive_Period
1161 * SwitchToSptDesired(S,G) is a policy function that is implementation
1162 * defined. An "infinite threshold" policy can be implemented by making
1163 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1164 * first packet" policy can be implemented by making
1165 * SwitchToSptDesired(S,G) return true once a single packet has been
1166 * received for the source and group.
1168 int pim_upstream_switch_to_spt_desired(struct prefix_sg
*sg
)
1170 if (I_am_RP(sg
->grp
))
1176 int pim_upstream_is_sg_rpt(struct pim_upstream
*up
)
1178 struct listnode
*chnode
;
1179 struct pim_ifchannel
*ch
;
1181 for (ALL_LIST_ELEMENTS_RO(up
->ifchannels
, chnode
, ch
)) {
1182 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1189 * After receiving a packet set SPTbit:
1191 * Update_SPTbit(S,G,iif) {
1192 * if ( iif == RPF_interface(S)
1193 * AND JoinDesired(S,G) == TRUE
1194 * AND ( DirectlyConnected(S) == TRUE
1195 * OR RPF_interface(S) != RPF_interface(RP(G))
1196 * OR inherited_olist(S,G,rpt) == NULL
1197 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1198 * ( RPF'(S,G) != NULL ) )
1199 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1200 * Set SPTbit(S,G) to TRUE
1204 void pim_upstream_set_sptbit(struct pim_upstream
*up
,
1205 struct interface
*incoming
)
1207 struct pim_upstream
*starup
= up
->parent
;
1209 // iif == RPF_interfvace(S)
1210 if (up
->rpf
.source_nexthop
.interface
!= incoming
) {
1211 if (PIM_DEBUG_TRACE
)
1213 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1214 __PRETTY_FUNCTION__
, incoming
->name
,
1215 up
->rpf
.source_nexthop
.interface
->name
);
1219 // AND JoinDesired(S,G) == TRUE
1222 // DirectlyConnected(S) == TRUE
1223 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1225 if (PIM_DEBUG_TRACE
)
1226 zlog_debug("%s: %s is directly connected to the source",
1227 __PRETTY_FUNCTION__
, up
->sg_str
);
1228 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1232 // OR RPF_interface(S) != RPF_interface(RP(G))
1234 || up
->rpf
.source_nexthop
1235 .interface
!= starup
->rpf
.source_nexthop
.interface
) {
1236 if (PIM_DEBUG_TRACE
)
1238 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1239 __PRETTY_FUNCTION__
, up
->sg_str
);
1240 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1244 // OR inherited_olist(S,G,rpt) == NULL
1245 if (pim_upstream_is_sg_rpt(up
)
1246 && pim_upstream_empty_inherited_olist(up
)) {
1247 if (PIM_DEBUG_TRACE
)
1248 zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
1249 __PRETTY_FUNCTION__
, up
->sg_str
);
1250 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1254 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1255 // ( RPF'(S,G) != NULL ) )
1256 if (up
->parent
&& pim_rpf_is_same(&up
->rpf
, &up
->parent
->rpf
)) {
1257 if (PIM_DEBUG_TRACE
)
1258 zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
1259 __PRETTY_FUNCTION__
, up
->sg_str
);
1260 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1267 const char *pim_upstream_state2str(enum pim_upstream_state join_state
)
1269 switch (join_state
) {
1270 case PIM_UPSTREAM_NOTJOINED
:
1273 case PIM_UPSTREAM_JOINED
:
1280 const char *pim_reg_state2str(enum pim_reg_state reg_state
, char *state_str
)
1282 switch (reg_state
) {
1283 case PIM_REG_NOINFO
:
1284 strcpy(state_str
, "RegNoInfo");
1287 strcpy(state_str
, "RegJoined");
1289 case PIM_REG_JOIN_PENDING
:
1290 strcpy(state_str
, "RegJoinPend");
1293 strcpy(state_str
, "RegPrune");
1296 strcpy(state_str
, "RegUnknown");
1301 static int pim_upstream_register_stop_timer(struct thread
*t
)
1303 struct pim_interface
*pim_ifp
;
1304 struct pim_upstream
*up
;
1305 struct pim_rpf
*rpg
;
1309 up
->t_rs_timer
= NULL
;
1311 if (PIM_DEBUG_TRACE
) {
1312 char state_str
[PIM_REG_STATE_STR_LEN
];
1313 zlog_debug("%s: (S,G)=%s upstream register stop timer %s",
1314 __PRETTY_FUNCTION__
, up
->sg_str
,
1315 pim_reg_state2str(up
->reg_state
, state_str
));
1318 switch (up
->reg_state
) {
1319 case PIM_REG_JOIN_PENDING
:
1320 up
->reg_state
= PIM_REG_JOIN
;
1321 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1322 PIM_OIF_FLAG_PROTO_PIM
);
1327 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1329 if (PIM_DEBUG_TRACE
)
1331 "%s: Interface: %s is not configured for pim",
1332 __PRETTY_FUNCTION__
,
1333 up
->rpf
.source_nexthop
.interface
->name
);
1336 up
->reg_state
= PIM_REG_JOIN_PENDING
;
1337 pim_upstream_start_register_stop_timer(up
, 1);
1339 if (((up
->channel_oil
->cc
.lastused
/ 100)
1340 > PIM_KEEPALIVE_PERIOD
)
1341 && (I_am_RP(up
->sg
.grp
))) {
1342 if (PIM_DEBUG_TRACE
)
1344 "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
1345 __PRETTY_FUNCTION__
);
1348 rpg
= RP(up
->sg
.grp
);
1349 memset(&ip_hdr
, 0, sizeof(struct ip
));
1350 ip_hdr
.ip_p
= PIM_IP_PROTO_PIM
;
1353 ip_hdr
.ip_src
= up
->sg
.src
;
1354 ip_hdr
.ip_dst
= up
->sg
.grp
;
1355 ip_hdr
.ip_len
= htons(20);
1356 // checksum is broken
1357 pim_register_send((uint8_t *)&ip_hdr
, sizeof(struct ip
),
1358 pim_ifp
->primary_address
, rpg
, 1, up
);
1367 void pim_upstream_start_register_stop_timer(struct pim_upstream
*up
,
1372 if (up
->t_rs_timer
) {
1373 THREAD_TIMER_OFF(up
->t_rs_timer
);
1374 up
->t_rs_timer
= NULL
;
1377 if (!null_register
) {
1378 uint32_t lower
= (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1379 uint32_t upper
= (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1380 time
= lower
+ (random() % (upper
- lower
+ 1))
1381 - PIM_REGISTER_PROBE_PERIOD
;
1383 time
= PIM_REGISTER_PROBE_PERIOD
;
1385 if (PIM_DEBUG_TRACE
) {
1387 "%s: (S,G)=%s Starting upstream register stop timer %d",
1388 __PRETTY_FUNCTION__
, up
->sg_str
, time
);
1390 THREAD_TIMER_ON(master
, up
->t_rs_timer
,
1391 pim_upstream_register_stop_timer
, up
, time
);
1394 int pim_upstream_inherited_olist_decide(struct pim_upstream
*up
)
1396 struct interface
*ifp
;
1397 struct pim_interface
*pim_ifp
= NULL
;
1398 struct pim_ifchannel
*ch
, *starch
;
1399 struct listnode
*node
;
1400 struct pim_upstream
*starup
= up
->parent
;
1401 int output_intf
= 0;
1403 if (up
->rpf
.source_nexthop
.interface
)
1404 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1406 if (PIM_DEBUG_TRACE
)
1407 zlog_debug("%s: up %s RPF is not present",
1408 __PRETTY_FUNCTION__
, up
->sg_str
);
1410 if (pim_ifp
&& !up
->channel_oil
)
1412 pim_channel_oil_add(&up
->sg
, pim_ifp
->mroute_vif_index
);
1414 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT
), node
, ifp
)) {
1418 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1421 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
1428 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1430 int flag
= PIM_OIF_FLAG_PROTO_PIM
;
1433 flag
= PIM_OIF_FLAG_PROTO_STAR
;
1435 pim_channel_add_oif(up
->channel_oil
, ifp
, flag
);
1444 * For a given upstream, determine the inherited_olist
1447 * inherited_olist(S,G,rpt) =
1448 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1449 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1450 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1452 * inherited_olist(S,G) =
1453 * inherited_olist(S,G,rpt) (+)
1454 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1456 * return 1 if there are any output interfaces
1457 * return 0 if there are not any output interfaces
1459 int pim_upstream_inherited_olist(struct pim_upstream
*up
)
1461 int output_intf
= pim_upstream_inherited_olist_decide(up
);
1464 * If we have output_intf switch state to Join and work like normal
1465 * If we don't have an output_intf that means we are probably a
1466 * switch on a stick so turn on forwarding to just accept the
1467 * incoming packets so we don't bother the other stuff!
1470 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
1477 int pim_upstream_empty_inherited_olist(struct pim_upstream
*up
)
1479 return pim_channel_oil_empty(up
->channel_oil
);
1483 * When we have a new neighbor,
1484 * find upstreams that don't have their rpf_addr
1485 * set and see if the new neighbor allows
1486 * the join to be sent
1488 void pim_upstream_find_new_rpf(void)
1490 struct listnode
*up_node
;
1491 struct listnode
*up_nextnode
;
1492 struct pim_upstream
*up
;
1495 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1497 for (ALL_LIST_ELEMENTS(pim_upstream_list
, up_node
, up_nextnode
, up
)) {
1498 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
1499 if (PIM_DEBUG_TRACE
)
1501 "Upstream %s without a path to send join, checking",
1503 pim_rpf_update(up
, NULL
, 1);
1508 static unsigned int pim_upstream_hash_key(void *arg
)
1510 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1512 return jhash_2words(up
->sg
.src
.s_addr
, up
->sg
.grp
.s_addr
, 0);
1515 void pim_upstream_terminate(void)
1517 if (pim_upstream_list
)
1518 list_delete(pim_upstream_list
);
1519 pim_upstream_list
= NULL
;
1521 if (pim_upstream_hash
)
1522 hash_free(pim_upstream_hash
);
1523 pim_upstream_hash
= NULL
;
1526 static int pim_upstream_equal(const void *arg1
, const void *arg2
)
1528 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
1529 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
1531 if ((up1
->sg
.grp
.s_addr
== up2
->sg
.grp
.s_addr
)
1532 && (up1
->sg
.src
.s_addr
== up2
->sg
.src
.s_addr
))
1538 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1539 * the cases where kat has to be restarted on rxing traffic -
1541 * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
1542 * set KeepaliveTimer(S,G) to Keepalive_Period
1543 * # Note: a register state transition or UpstreamJPState(S,G)
1544 * # transition may happen as a result of restarting
1545 * # KeepaliveTimer, and must be dealt with here.
1547 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1548 * inherited_olist(S,G) != NULL ) {
1549 * set KeepaliveTimer(S,G) to Keepalive_Period
1552 static bool pim_upstream_kat_start_ok(struct pim_upstream
*up
)
1554 /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
1555 * so we will skip that here */
1556 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1561 if ((up
->join_state
== PIM_UPSTREAM_JOINED
)
1562 && !pim_upstream_empty_inherited_olist(up
)) {
1563 /* XXX: I have added this RP check just for 3.2 and it's a
1565 * what rfc-4601 says. Till now we were only running KAT on FHR
1567 * there is some angst around making the change to run it all
1569 * maintain the (S, G) state. This is tracked via CM-13601 and
1571 * removed to handle spt turn-arounds correctly in a 3-tier clos
1573 if (I_am_RP(up
->sg
.grp
))
1581 * Code to check and see if we've received packets on a S,G mroute
1582 * and if so to set the SPT bit appropriately
1584 static void pim_upstream_sg_running(void *arg
)
1586 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1588 // No packet can have arrived here if this is the case
1589 if (!up
->channel_oil
|| !up
->channel_oil
->installed
) {
1590 if (PIM_DEBUG_TRACE
)
1591 zlog_debug("%s: %s is not installed in mroute",
1592 __PRETTY_FUNCTION__
, up
->sg_str
);
1597 * This is a bit of a hack
1598 * We've noted that we should rescan but
1599 * we've missed the window for doing so in
1600 * pim_zebra.c for some reason. I am
1601 * only doing this at this point in time
1602 * to get us up and working for the moment
1604 if (up
->channel_oil
->oil_inherited_rescan
) {
1605 if (PIM_DEBUG_TRACE
)
1607 "%s: Handling unscanned inherited_olist for %s",
1608 __PRETTY_FUNCTION__
, up
->sg_str
);
1609 pim_upstream_inherited_olist_decide(up
);
1610 up
->channel_oil
->oil_inherited_rescan
= 0;
1612 pim_mroute_update_counters(up
->channel_oil
);
1614 // Have we seen packets?
1615 if ((up
->channel_oil
->cc
.oldpktcnt
>= up
->channel_oil
->cc
.pktcnt
)
1616 && (up
->channel_oil
->cc
.lastused
/ 100 > 30)) {
1617 if (PIM_DEBUG_TRACE
) {
1619 "%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
1620 __PRETTY_FUNCTION__
, up
->sg_str
,
1621 up
->channel_oil
->cc
.oldpktcnt
,
1622 up
->channel_oil
->cc
.pktcnt
,
1623 up
->channel_oil
->cc
.lastused
/ 100);
1628 if (pim_upstream_kat_start_ok(up
)) {
1629 /* Add a source reference to the stream if
1630 * one doesn't already exist */
1631 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1632 if (PIM_DEBUG_TRACE
)
1634 "source reference created on kat restart %s",
1637 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM
,
1638 __PRETTY_FUNCTION__
);
1639 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
1640 pim_upstream_fhr_kat_start(up
);
1642 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
1643 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
))
1644 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
1646 if (up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
) {
1647 pim_upstream_set_sptbit(up
, up
->rpf
.source_nexthop
.interface
);
1652 void pim_upstream_add_lhr_star_pimreg(void)
1654 struct pim_upstream
*up
;
1655 struct listnode
*node
;
1657 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, node
, up
)) {
1658 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
1661 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
))
1664 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1665 PIM_OIF_FLAG_PROTO_IGMP
);
1669 void pim_upstream_spt_prefix_list_update(struct prefix_list
*pl
)
1671 const char *pname
= prefix_list_name(pl
);
1673 if (pimg
->spt
.plist
&& strcmp(pimg
->spt
.plist
, pname
) == 0) {
1674 pim_upstream_remove_lhr_star_pimreg(pname
);
1679 * nlist -> The new prefix list
1681 * Per Group Application of pimreg to the OIL
1682 * If the prefix list tells us DENY then
1683 * we need to Switchover to SPT immediate
1684 * so add the pimreg.
1685 * If the prefix list tells us to ACCEPT than
1686 * we need to Never do the SPT so remove
1690 void pim_upstream_remove_lhr_star_pimreg(const char *nlist
)
1692 struct pim_upstream
*up
;
1693 struct listnode
*node
;
1694 struct prefix_list
*np
;
1696 enum prefix_list_type apply_new
;
1698 np
= prefix_list_lookup(AFI_IP
, nlist
);
1701 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1703 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list
, node
, up
)) {
1704 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
1707 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
))
1711 pim_channel_del_oif(up
->channel_oil
, pim_regiface
,
1712 PIM_OIF_FLAG_PROTO_IGMP
);
1715 g
.u
.prefix4
= up
->sg
.grp
;
1716 apply_new
= prefix_list_apply(np
, &g
);
1717 if (apply_new
== PREFIX_DENY
)
1718 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1719 PIM_OIF_FLAG_PROTO_IGMP
);
1721 pim_channel_del_oif(up
->channel_oil
, pim_regiface
,
1722 PIM_OIF_FLAG_PROTO_IGMP
);
1726 void pim_upstream_init(void)
1728 pim_upstream_sg_wheel
=
1729 wheel_init(master
, 31000, 100, pim_upstream_hash_key
,
1730 pim_upstream_sg_running
);
1731 pim_upstream_hash
= hash_create_size(8192, pim_upstream_hash_key
,
1732 pim_upstream_equal
);
1734 pim_upstream_list
= list_new();
1735 pim_upstream_list
->del
= (void (*)(void *))pim_upstream_free
;
1736 pim_upstream_list
->cmp
= pim_upstream_compare
;