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
37 #include "pim_iface.h"
39 #include "pim_zlookup.h"
40 #include "pim_upstream.h"
41 #include "pim_ifchannel.h"
42 #include "pim_neighbor.h"
44 #include "pim_zebra.h"
46 #include "pim_macro.h"
49 #include "pim_register.h"
51 #include "pim_jp_agg.h"
55 static void join_timer_stop(struct pim_upstream
*up
);
57 pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
);
60 * A (*,G) or a (*,*) is going away
61 * remove the parent pointer from
62 * those pointing at us
64 static void pim_upstream_remove_children(struct pim_instance
*pim
,
65 struct pim_upstream
*up
)
67 struct pim_upstream
*child
;
72 while (!list_isempty(up
->sources
)) {
73 child
= listnode_head(up
->sources
);
74 listnode_delete(up
->sources
, child
);
75 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child
->flags
)) {
76 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child
->flags
);
77 child
= pim_upstream_del(pim
, child
,
83 list_delete(&up
->sources
);
87 * A (*,G) or a (*,*) is being created
88 * Find the children that would point
91 static void pim_upstream_find_new_children(struct pim_instance
*pim
,
92 struct pim_upstream
*up
)
94 struct pim_upstream
*child
;
95 struct listnode
*ch_node
;
97 if ((up
->sg
.src
.s_addr
!= INADDR_ANY
)
98 && (up
->sg
.grp
.s_addr
!= INADDR_ANY
))
101 if ((up
->sg
.src
.s_addr
== INADDR_ANY
)
102 && (up
->sg
.grp
.s_addr
== INADDR_ANY
))
105 for (ALL_LIST_ELEMENTS_RO(pim
->upstream_list
, ch_node
, child
)) {
106 if ((up
->sg
.grp
.s_addr
!= INADDR_ANY
)
107 && (child
->sg
.grp
.s_addr
== up
->sg
.grp
.s_addr
)
110 listnode_add_sort(up
->sources
, child
);
116 * If we have a (*,*) || (S,*) there is no parent
117 * If we have a (S,G), find the (*,G)
118 * If we have a (*,G), find the (*,*)
120 static struct pim_upstream
*pim_upstream_find_parent(struct pim_instance
*pim
,
121 struct pim_upstream
*child
)
123 struct prefix_sg any
= child
->sg
;
124 struct pim_upstream
*up
= NULL
;
127 if ((child
->sg
.src
.s_addr
!= INADDR_ANY
)
128 && (child
->sg
.grp
.s_addr
!= INADDR_ANY
)) {
129 any
.src
.s_addr
= INADDR_ANY
;
130 up
= pim_upstream_find(pim
, &any
);
133 listnode_add(up
->sources
, child
);
141 static void upstream_channel_oil_detach(struct pim_upstream
*up
)
143 if (up
->channel_oil
) {
144 /* Detaching from channel_oil, channel_oil may exist post del,
145 but upstream would not keep reference of it
147 up
->channel_oil
->up
= NULL
;
148 pim_channel_oil_del(up
->channel_oil
);
149 up
->channel_oil
= NULL
;
153 struct pim_upstream
*pim_upstream_del(struct pim_instance
*pim
,
154 struct pim_upstream
*up
, const char *name
)
156 struct listnode
*node
, *nnode
;
157 struct pim_ifchannel
*ch
;
158 bool notify_msdp
= false;
163 "%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
164 __PRETTY_FUNCTION__
, name
, up
->sg_str
, pim
->vrf
->name
,
165 up
->ref_count
, up
->flags
,
166 up
->channel_oil
->oil_ref_count
);
168 assert(up
->ref_count
> 0);
172 if (up
->ref_count
>= 1)
175 THREAD_OFF(up
->t_ka_timer
);
176 THREAD_OFF(up
->t_rs_timer
);
177 THREAD_OFF(up
->t_msdp_reg_timer
);
179 if (up
->join_state
== PIM_UPSTREAM_JOINED
) {
180 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 0);
182 if (up
->sg
.src
.s_addr
== INADDR_ANY
) {
183 /* if a (*, G) entry in the joined state is being
185 * need to notify MSDP */
191 pim_jp_agg_upstream_verification(up
, false);
192 up
->rpf
.source_nexthop
.interface
= NULL
;
194 if (up
->sg
.src
.s_addr
!= INADDR_ANY
) {
195 if (pim
->upstream_sg_wheel
)
196 wheel_remove_item(pim
->upstream_sg_wheel
, up
);
200 pim_mroute_del(up
->channel_oil
, __PRETTY_FUNCTION__
);
201 upstream_channel_oil_detach(up
);
203 for (ALL_LIST_ELEMENTS(up
->ifchannels
, node
, nnode
, ch
))
204 pim_ifchannel_delete(ch
);
205 list_delete(&up
->ifchannels
);
207 pim_upstream_remove_children(pim
, up
);
209 list_delete(&up
->sources
);
211 if (up
->parent
&& up
->parent
->sources
)
212 listnode_delete(up
->parent
->sources
, up
);
215 listnode_delete(pim
->upstream_list
, up
);
216 hash_release(pim
->upstream_hash
, up
);
219 pim_msdp_up_del(pim
, &up
->sg
);
222 /* Deregister addr with Zebra NHT */
223 nht_p
.family
= AF_INET
;
224 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
225 nht_p
.u
.prefix4
= up
->upstream_addr
;
226 if (PIM_DEBUG_TRACE
) {
227 char buf
[PREFIX2STR_BUFFER
];
228 prefix2str(&nht_p
, buf
, sizeof(buf
));
229 zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT",
230 __PRETTY_FUNCTION__
, up
->sg_str
, buf
);
232 pim_delete_tracked_nexthop(pim
, &nht_p
, up
, NULL
);
234 XFREE(MTYPE_PIM_UPSTREAM
, up
);
239 void pim_upstream_send_join(struct pim_upstream
*up
)
241 if (PIM_DEBUG_TRACE
) {
242 char rpf_str
[PREFIX_STRLEN
];
243 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
245 zlog_debug("%s: RPF'%s=%s(%s) for Interface %s",
246 __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
,
247 pim_upstream_state2str(up
->join_state
),
248 up
->rpf
.source_nexthop
.interface
->name
);
249 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
250 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
251 __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
);
256 /* send Join(S,G) to the current upstream neighbor */
257 pim_jp_agg_single_upstream_send(&up
->rpf
, up
, 1 /* join */);
260 static int on_join_timer(struct thread
*t
)
262 struct pim_upstream
*up
;
267 * In the case of a HFR we will not ahve anyone to send this to.
269 if (PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
273 * Don't send the join if the outgoing interface is a loopback
274 * But since this might change leave the join timer running
276 if (up
->rpf
.source_nexthop
277 .interface
&& !if_is_loopback(up
->rpf
.source_nexthop
.interface
))
278 pim_upstream_send_join(up
);
280 join_timer_start(up
);
285 static void join_timer_stop(struct pim_upstream
*up
)
287 struct pim_neighbor
*nbr
;
289 THREAD_OFF(up
->t_join_timer
);
291 nbr
= pim_neighbor_find(up
->rpf
.source_nexthop
.interface
,
292 up
->rpf
.rpf_addr
.u
.prefix4
);
295 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
);
297 pim_jp_agg_upstream_verification(up
, false);
300 void join_timer_start(struct pim_upstream
*up
)
302 struct pim_neighbor
*nbr
= NULL
;
304 if (up
->rpf
.source_nexthop
.interface
) {
305 nbr
= pim_neighbor_find(up
->rpf
.source_nexthop
.interface
,
306 up
->rpf
.rpf_addr
.u
.prefix4
);
308 if (PIM_DEBUG_PIM_EVENTS
) {
310 "%s: starting %d sec timer for upstream (S,G)=%s",
311 __PRETTY_FUNCTION__
, qpim_t_periodic
,
317 pim_jp_agg_add_group(nbr
->upstream_jp_agg
, up
, 1);
319 THREAD_OFF(up
->t_join_timer
);
320 thread_add_timer(router
->master
, on_join_timer
, up
,
321 qpim_t_periodic
, &up
->t_join_timer
);
323 pim_jp_agg_upstream_verification(up
, true);
327 * This is only called when we are switching the upstream
328 * J/P from one neighbor to another
330 * As such we need to remove from the old list and
331 * add to the new list.
333 void pim_upstream_join_timer_restart(struct pim_upstream
*up
,
336 // THREAD_OFF(up->t_join_timer);
337 join_timer_start(up
);
340 static void pim_upstream_join_timer_restart_msec(struct pim_upstream
*up
,
343 if (PIM_DEBUG_PIM_EVENTS
) {
344 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
345 __PRETTY_FUNCTION__
, interval_msec
, up
->sg_str
);
348 THREAD_OFF(up
->t_join_timer
);
349 thread_add_timer_msec(router
->master
, on_join_timer
, up
, interval_msec
,
353 void pim_upstream_join_suppress(struct pim_upstream
*up
,
354 struct in_addr rpf_addr
, int holdtime
)
356 long t_joinsuppress_msec
;
357 long join_timer_remain_msec
;
359 t_joinsuppress_msec
=
360 MIN(pim_if_t_suppressed_msec(up
->rpf
.source_nexthop
.interface
),
363 join_timer_remain_msec
= pim_time_timer_remain_msec(up
->t_join_timer
);
365 if (PIM_DEBUG_TRACE
) {
366 char rpf_str
[INET_ADDRSTRLEN
];
367 pim_inet4_dump("<rpf?>", rpf_addr
, rpf_str
, sizeof(rpf_str
));
369 "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
370 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, rpf_str
,
371 join_timer_remain_msec
, t_joinsuppress_msec
);
374 if (join_timer_remain_msec
< t_joinsuppress_msec
) {
375 if (PIM_DEBUG_TRACE
) {
377 "%s %s: suppressing Join(S,G)=%s for %ld msec",
378 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
,
379 t_joinsuppress_msec
);
382 pim_upstream_join_timer_restart_msec(up
, t_joinsuppress_msec
);
386 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label
,
387 struct pim_upstream
*up
)
389 long join_timer_remain_msec
;
392 join_timer_remain_msec
= pim_time_timer_remain_msec(up
->t_join_timer
);
394 pim_if_t_override_msec(up
->rpf
.source_nexthop
.interface
);
396 if (PIM_DEBUG_TRACE
) {
397 char rpf_str
[INET_ADDRSTRLEN
];
398 pim_inet4_dump("<rpf?>", up
->rpf
.rpf_addr
.u
.prefix4
, rpf_str
,
401 "%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
402 debug_label
, up
->sg_str
, rpf_str
,
403 join_timer_remain_msec
, t_override_msec
);
406 if (join_timer_remain_msec
> t_override_msec
) {
407 if (PIM_DEBUG_TRACE
) {
409 "%s: decreasing (S,G)=%s join timer to t_override=%d msec",
410 debug_label
, up
->sg_str
, t_override_msec
);
413 pim_upstream_join_timer_restart_msec(up
, t_override_msec
);
417 static void forward_on(struct pim_upstream
*up
)
419 struct listnode
*chnode
;
420 struct listnode
*chnextnode
;
421 struct pim_ifchannel
*ch
= NULL
;
423 /* scan (S,G) state */
424 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
425 if (pim_macro_chisin_oiflist(ch
))
426 pim_forward_start(ch
);
428 } /* scan iface channel list */
431 static void forward_off(struct pim_upstream
*up
)
433 struct listnode
*chnode
;
434 struct listnode
*chnextnode
;
435 struct pim_ifchannel
*ch
;
437 /* scan per-interface (S,G) state */
438 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
440 pim_forward_stop(ch
, false);
442 } /* scan iface channel list */
445 static int pim_upstream_could_register(struct pim_upstream
*up
)
447 struct pim_interface
*pim_ifp
= NULL
;
449 if (up
->rpf
.source_nexthop
.interface
)
450 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
453 zlog_debug("%s: up %s RPF is not present",
454 __PRETTY_FUNCTION__
, up
->sg_str
);
457 if (pim_ifp
&& PIM_I_am_DR(pim_ifp
)
458 && pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
465 /* Source registration is suppressed for SSM groups. When the SSM range changes
466 * we re-revaluate register setup for existing upstream entries */
467 void pim_upstream_register_reevaluate(struct pim_instance
*pim
)
469 struct listnode
*upnode
;
470 struct pim_upstream
*up
;
472 for (ALL_LIST_ELEMENTS_RO(pim
->upstream_list
, upnode
, up
)) {
473 /* If FHR is set CouldRegister is True. Also check if the flow
474 * is actually active; if it is not kat setup will trigger
476 * registration whenever the flow becomes active. */
477 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
) || !up
->t_ka_timer
)
480 if (pim_is_grp_ssm(pim
, up
->sg
.grp
)) {
481 /* clear the register state for SSM groups */
482 if (up
->reg_state
!= PIM_REG_NOINFO
) {
483 if (PIM_DEBUG_PIM_EVENTS
)
485 "Clear register for %s as G is now SSM",
487 /* remove regiface from the OIL if it is there*/
488 pim_channel_del_oif(up
->channel_oil
,
490 PIM_OIF_FLAG_PROTO_PIM
);
491 up
->reg_state
= PIM_REG_NOINFO
;
494 /* register ASM sources with the RP */
495 if (up
->reg_state
== PIM_REG_NOINFO
) {
496 if (PIM_DEBUG_PIM_EVENTS
)
498 "Register %s as G is now ASM",
500 pim_channel_add_oif(up
->channel_oil
,
502 PIM_OIF_FLAG_PROTO_PIM
);
503 up
->reg_state
= PIM_REG_JOIN
;
509 void pim_upstream_switch(struct pim_instance
*pim
, struct pim_upstream
*up
,
510 enum pim_upstream_state new_state
)
512 enum pim_upstream_state old_state
= up
->join_state
;
514 if (PIM_DEBUG_PIM_EVENTS
) {
515 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
516 __PRETTY_FUNCTION__
, up
->sg_str
,
517 pim_upstream_state2str(up
->join_state
),
518 pim_upstream_state2str(new_state
));
521 up
->join_state
= new_state
;
522 if (old_state
!= new_state
)
523 up
->state_transition
= pim_time_monotonic_sec();
525 pim_upstream_update_assert_tracking_desired(up
);
527 if (new_state
== PIM_UPSTREAM_JOINED
) {
528 if (old_state
!= PIM_UPSTREAM_JOINED
) {
529 int old_fhr
= PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
);
531 pim_msdp_up_join_state_changed(pim
, up
);
532 if (pim_upstream_could_register(up
)) {
533 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
535 && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(
537 pim_upstream_keep_alive_timer_start(
538 up
, pim
->keep_alive_time
);
539 pim_register_join(up
);
542 pim_upstream_send_join(up
);
543 join_timer_start(up
);
551 if (old_state
== PIM_UPSTREAM_JOINED
)
552 pim_msdp_up_join_state_changed(pim
, up
);
554 /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
556 If I am RP for G then send S,G prune to its IIF. */
557 if (pim_upstream_is_sg_rpt(up
) && up
->parent
558 && !I_am_RP(pim
, up
->sg
.grp
)) {
559 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
561 "%s: *,G IIF %s S,G IIF %s ",
563 up
->parent
->rpf
.source_nexthop
565 up
->rpf
.source_nexthop
.interface
->name
);
566 pim_jp_agg_single_upstream_send(&up
->parent
->rpf
,
570 pim_jp_agg_single_upstream_send(&up
->rpf
, up
,
576 int pim_upstream_compare(void *arg1
, void *arg2
)
578 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
579 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
581 if (ntohl(up1
->sg
.grp
.s_addr
) < ntohl(up2
->sg
.grp
.s_addr
))
584 if (ntohl(up1
->sg
.grp
.s_addr
) > ntohl(up2
->sg
.grp
.s_addr
))
587 if (ntohl(up1
->sg
.src
.s_addr
) < ntohl(up2
->sg
.src
.s_addr
))
590 if (ntohl(up1
->sg
.src
.s_addr
) > ntohl(up2
->sg
.src
.s_addr
))
596 static struct pim_upstream
*pim_upstream_new(struct pim_instance
*pim
,
597 struct prefix_sg
*sg
,
598 struct interface
*incoming
,
600 struct pim_ifchannel
*ch
)
602 enum pim_rpf_result rpf_result
;
603 struct pim_interface
*pim_ifp
;
604 struct pim_upstream
*up
;
606 up
= XCALLOC(MTYPE_PIM_UPSTREAM
, sizeof(*up
));
609 pim_str_sg_set(sg
, up
->sg_str
);
613 up
= hash_get(pim
->upstream_hash
, up
, hash_alloc_intern
);
614 if (!pim_rp_set_upstream_addr(pim
, &up
->upstream_addr
, sg
->src
,
617 zlog_debug("%s: Received a (*,G) with no RP configured",
618 __PRETTY_FUNCTION__
);
620 hash_release(pim
->upstream_hash
, up
);
621 XFREE(MTYPE_PIM_UPSTREAM
, up
);
625 up
->parent
= pim_upstream_find_parent(pim
, up
);
626 if (up
->sg
.src
.s_addr
== INADDR_ANY
) {
627 up
->sources
= list_new();
628 up
->sources
->cmp
= pim_upstream_compare
;
632 pim_upstream_find_new_children(pim
, up
);
635 up
->t_join_timer
= NULL
;
636 up
->t_ka_timer
= NULL
;
637 up
->t_rs_timer
= NULL
;
638 up
->t_msdp_reg_timer
= NULL
;
639 up
->join_state
= PIM_UPSTREAM_NOTJOINED
;
640 up
->reg_state
= PIM_REG_NOINFO
;
641 up
->state_transition
= pim_time_monotonic_sec();
642 up
->channel_oil
= NULL
;
643 up
->sptbit
= PIM_UPSTREAM_SPTBIT_FALSE
;
645 up
->rpf
.source_nexthop
.interface
= NULL
;
646 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.family
= AF_INET
;
647 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
.s_addr
=
649 up
->rpf
.source_nexthop
.mrib_metric_preference
=
650 qpim_infinite_assert_metric
.metric_preference
;
651 up
->rpf
.source_nexthop
.mrib_route_metric
=
652 qpim_infinite_assert_metric
.route_metric
;
653 up
->rpf
.rpf_addr
.family
= AF_INET
;
654 up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
= PIM_NET_INADDR_ANY
;
656 up
->ifchannels
= list_new();
657 up
->ifchannels
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
659 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
660 wheel_add_item(pim
->upstream_sg_wheel
, up
);
662 rpf_result
= pim_rpf_update(pim
, up
, NULL
, 1);
663 if (rpf_result
== PIM_RPF_FAILURE
) {
668 "%s: Attempting to create upstream(%s), Unable to RPF for source",
669 __PRETTY_FUNCTION__
, up
->sg_str
);
671 nht_p
.family
= AF_INET
;
672 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
673 nht_p
.u
.prefix4
= up
->upstream_addr
;
674 pim_delete_tracked_nexthop(pim
, &nht_p
, up
, NULL
);
677 listnode_delete(up
->parent
->sources
, up
);
681 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
682 wheel_remove_item(pim
->upstream_sg_wheel
, up
);
684 pim_upstream_remove_children(pim
, up
);
686 list_delete(&up
->sources
);
688 list_delete(&up
->ifchannels
);
690 hash_release(pim
->upstream_hash
, up
);
691 XFREE(MTYPE_PIM_UPSTREAM
, up
);
695 if (up
->rpf
.source_nexthop
.interface
) {
696 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
698 up
->channel_oil
= pim_channel_oil_add(
699 pim
, &up
->sg
, pim_ifp
->mroute_vif_index
);
701 listnode_add_sort(pim
->upstream_list
, up
);
703 if (PIM_DEBUG_TRACE
) {
705 "%s: Created Upstream %s upstream_addr %s ref count %d increment",
706 __PRETTY_FUNCTION__
, up
->sg_str
,
707 inet_ntoa(up
->upstream_addr
), up
->ref_count
);
713 struct pim_upstream
*pim_upstream_find(struct pim_instance
*pim
,
714 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
;
729 struct pim_interface
*pim_ifp
;
731 pim_ifp
= incoming
->info
;
733 up
= pim_upstream_find(pim_ifp
->pim
, 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(pim_ifp
->pim
, sg
, incoming
, flags
, name
,
752 void pim_upstream_ref(struct pim_upstream
*up
, int flags
, const char *name
)
757 zlog_debug("%s(%s): upstream %s ref count %d increment",
758 __PRETTY_FUNCTION__
, name
, up
->sg_str
,
762 struct pim_upstream
*pim_upstream_add(struct pim_instance
*pim
,
763 struct prefix_sg
*sg
,
764 struct interface
*incoming
, int flags
,
766 struct pim_ifchannel
*ch
)
768 struct pim_upstream
*up
= NULL
;
771 up
= pim_upstream_find(pim
, sg
);
773 pim_upstream_ref(up
, flags
, name
);
776 up
= pim_upstream_new(pim
, sg
, incoming
, flags
, ch
);
779 if (PIM_DEBUG_TRACE
) {
781 char buf
[PREFIX2STR_BUFFER
];
782 prefix2str(&up
->rpf
.rpf_addr
, buf
, sizeof(buf
));
783 zlog_debug("%s(%s): %s, iif %s (%s) found: %d: ref_count: %d",
784 __PRETTY_FUNCTION__
, name
,
785 up
->sg_str
, buf
, up
->rpf
.source_nexthop
.interface
?
786 up
->rpf
.source_nexthop
.interface
->name
: "NIL" ,
787 found
, up
->ref_count
);
789 zlog_debug("%s(%s): (%s) failure to create",
790 __PRETTY_FUNCTION__
, name
,
791 pim_str_sg_dump(sg
));
798 * Passed in up must be the upstream for ch. starch is NULL if no
801 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream
*up
,
802 struct pim_ifchannel
*ch
,
803 struct pim_ifchannel
*starch
)
806 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
809 if (!pim_macro_ch_lost_assert(ch
)
810 && pim_macro_chisin_joins_or_include(ch
))
818 if (PIM_IF_FLAG_TEST_S_G_RPT(starch
->upstream
->flags
))
821 if (!pim_macro_ch_lost_assert(starch
)
822 && pim_macro_chisin_joins_or_include(starch
))
830 Evaluate JoinDesired(S,G):
832 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
835 inherited_olist(S,G) =
836 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
838 JoinDesired(S,G) may be affected by changes in the following:
840 pim_ifp->primary_address
842 ch->ifassert_winner_metric
844 ch->local_ifmembership
846 ch->upstream->rpf.source_nexthop.mrib_metric_preference
847 ch->upstream->rpf.source_nexthop.mrib_route_metric
848 ch->upstream->rpf.source_nexthop.interface
850 See also pim_upstream_update_join_desired() below.
852 int pim_upstream_evaluate_join_desired(struct pim_instance
*pim
,
853 struct pim_upstream
*up
)
855 struct interface
*ifp
;
856 struct pim_ifchannel
*ch
, *starch
;
857 struct pim_upstream
*starup
= up
->parent
;
860 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
864 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
867 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
874 ret
+= pim_upstream_evaluate_join_desired_interface(up
, ch
,
876 } /* scan iface channel list */
878 return ret
; /* false */
882 See also pim_upstream_evaluate_join_desired() above.
884 void pim_upstream_update_join_desired(struct pim_instance
*pim
,
885 struct pim_upstream
*up
)
887 int was_join_desired
; /* boolean */
888 int is_join_desired
; /* boolean */
890 was_join_desired
= PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up
->flags
);
892 is_join_desired
= pim_upstream_evaluate_join_desired(pim
, up
);
894 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up
->flags
);
896 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up
->flags
);
898 /* switched from false to true */
899 if (is_join_desired
&& !was_join_desired
) {
900 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_JOINED
);
904 /* switched from true to false */
905 if (!is_join_desired
&& was_join_desired
) {
906 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
912 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
913 Transitions from Joined State
914 RPF'(S,G) GenID changes
916 The upstream (S,G) state machine remains in Joined state. If the
917 Join Timer is set to expire in more than t_override seconds, reset
918 it so that it expires after t_override seconds.
920 void pim_upstream_rpf_genid_changed(struct pim_instance
*pim
,
921 struct in_addr neigh_addr
)
923 struct listnode
*up_node
;
924 struct listnode
*up_nextnode
;
925 struct pim_upstream
*up
;
928 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
930 for (ALL_LIST_ELEMENTS(pim
->upstream_list
, up_node
, up_nextnode
, up
)) {
932 if (PIM_DEBUG_TRACE
) {
933 char neigh_str
[INET_ADDRSTRLEN
];
934 char rpf_addr_str
[PREFIX_STRLEN
];
935 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
937 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_addr_str
,
938 sizeof(rpf_addr_str
));
940 "%s: matching neigh=%s against upstream (S,G)=%s[%s] joined=%d rpf_addr=%s",
941 __PRETTY_FUNCTION__
, neigh_str
, up
->sg_str
,
943 up
->join_state
== PIM_UPSTREAM_JOINED
,
947 /* consider only (S,G) upstream in Joined state */
948 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
951 /* match RPF'(S,G)=neigh_addr */
952 if (up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
!= neigh_addr
.s_addr
)
955 pim_upstream_join_timer_decrease_to_t_override(
956 "RPF'(S,G) GenID change", up
);
961 void pim_upstream_rpf_interface_changed(struct pim_upstream
*up
,
962 struct interface
*old_rpf_ifp
)
964 struct listnode
*chnode
;
965 struct listnode
*chnextnode
;
966 struct pim_ifchannel
*ch
;
968 /* search all ifchannels */
969 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
970 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
972 /* RPF_interface(S) was NOT I */
973 (old_rpf_ifp
== ch
->interface
) &&
974 /* RPF_interface(S) stopped being I */
975 (ch
->upstream
->rpf
.source_nexthop
976 .interface
!= ch
->interface
)) {
977 assert_action_a5(ch
);
979 } /* PIM_IFASSERT_I_AM_LOSER */
981 pim_ifchannel_update_assert_tracking_desired(ch
);
985 void pim_upstream_update_could_assert(struct pim_upstream
*up
)
987 struct listnode
*chnode
;
988 struct listnode
*chnextnode
;
989 struct pim_ifchannel
*ch
;
991 /* scan per-interface (S,G) state */
992 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
993 pim_ifchannel_update_could_assert(ch
);
994 } /* scan iface channel list */
997 void pim_upstream_update_my_assert_metric(struct pim_upstream
*up
)
999 struct listnode
*chnode
;
1000 struct listnode
*chnextnode
;
1001 struct pim_ifchannel
*ch
;
1003 /* scan per-interface (S,G) state */
1004 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1005 pim_ifchannel_update_my_assert_metric(ch
);
1007 } /* scan iface channel list */
1010 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream
*up
)
1012 struct listnode
*chnode
;
1013 struct listnode
*chnextnode
;
1014 struct pim_interface
*pim_ifp
;
1015 struct pim_ifchannel
*ch
;
1017 /* scan per-interface (S,G) state */
1018 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
1021 pim_ifp
= ch
->interface
->info
;
1025 pim_ifchannel_update_assert_tracking_desired(ch
);
1027 } /* scan iface channel list */
1030 /* When kat is stopped CouldRegister goes to false so we need to
1031 * transition the (S, G) on FHR to NI state and remove reg tunnel
1033 static void pim_upstream_fhr_kat_expiry(struct pim_instance
*pim
,
1034 struct pim_upstream
*up
)
1036 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
1039 if (PIM_DEBUG_TRACE
)
1040 zlog_debug("kat expired on %s; clear fhr reg state",
1043 /* stop reg-stop timer */
1044 THREAD_OFF(up
->t_rs_timer
);
1045 /* remove regiface from the OIL if it is there*/
1046 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
1047 PIM_OIF_FLAG_PROTO_PIM
);
1048 /* clear the register state */
1049 up
->reg_state
= PIM_REG_NOINFO
;
1050 PIM_UPSTREAM_FLAG_UNSET_FHR(up
->flags
);
1053 /* When kat is started CouldRegister can go to true. And if it does we
1054 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
1056 static void pim_upstream_fhr_kat_start(struct pim_upstream
*up
)
1058 if (pim_upstream_could_register(up
)) {
1059 if (PIM_DEBUG_TRACE
)
1061 "kat started on %s; set fhr reg state to joined",
1064 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
1065 if (up
->reg_state
== PIM_REG_NOINFO
)
1066 pim_register_join(up
);
1071 * On an RP, the PMBR value must be cleared when the
1072 * Keepalive Timer expires
1073 * KAT expiry indicates that flow is inactive. If the flow was created or
1074 * maintained by activity now is the time to deref it.
1076 static int pim_upstream_keep_alive_timer(struct thread
*t
)
1078 struct pim_upstream
*up
;
1079 struct pim_instance
*pim
;
1082 pim
= up
->channel_oil
->pim
;
1084 if (I_am_RP(pim
, up
->sg
.grp
)) {
1085 pim_br_clear_pmbr(&up
->sg
);
1087 * We need to do more here :)
1088 * But this is the start.
1092 /* source is no longer active - pull the SA from MSDP's cache */
1093 pim_msdp_sa_local_del(pim
, &up
->sg
);
1095 /* if entry was created because of activity we need to deref it */
1096 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1097 pim_upstream_fhr_kat_expiry(pim
, up
);
1098 if (PIM_DEBUG_TRACE
)
1100 "kat expired on %s[%s]; remove stream reference",
1101 up
->sg_str
, pim
->vrf
->name
);
1102 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up
->flags
);
1103 pim_upstream_del(pim
, up
, __PRETTY_FUNCTION__
);
1104 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
)) {
1105 struct pim_upstream
*parent
= up
->parent
;
1107 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up
->flags
);
1108 pim_upstream_del(pim
, up
, __PRETTY_FUNCTION__
);
1111 pim_jp_agg_single_upstream_send(&parent
->rpf
, parent
,
1119 void pim_upstream_keep_alive_timer_start(struct pim_upstream
*up
, uint32_t time
)
1121 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1122 if (PIM_DEBUG_TRACE
)
1123 zlog_debug("kat start on %s with no stream reference",
1126 THREAD_OFF(up
->t_ka_timer
);
1127 thread_add_timer(router
->master
, pim_upstream_keep_alive_timer
, up
,
1128 time
, &up
->t_ka_timer
);
1130 /* any time keepalive is started against a SG we will have to
1131 * re-evaluate our active source database */
1132 pim_msdp_sa_local_update(up
);
1135 /* MSDP on RP needs to know if a source is registerable to this RP */
1136 static int pim_upstream_msdp_reg_timer(struct thread
*t
)
1138 struct pim_upstream
*up
= THREAD_ARG(t
);
1139 struct pim_instance
*pim
= up
->channel_oil
->pim
;
1141 /* source is no longer active - pull the SA from MSDP's cache */
1142 pim_msdp_sa_local_del(pim
, &up
->sg
);
1145 void pim_upstream_msdp_reg_timer_start(struct pim_upstream
*up
)
1147 THREAD_OFF(up
->t_msdp_reg_timer
);
1148 thread_add_timer(router
->master
, pim_upstream_msdp_reg_timer
, up
,
1149 PIM_MSDP_REG_RXED_PERIOD
, &up
->t_msdp_reg_timer
);
1151 pim_msdp_sa_local_update(up
);
1155 * 4.2.1 Last-Hop Switchover to the SPT
1157 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1158 * RP. Once traffic from sources to joined groups arrives at a last-hop
1159 * router, it has the option of switching to receive the traffic on a
1160 * shortest path tree (SPT).
1162 * The decision for a router to switch to the SPT is controlled as
1166 * CheckSwitchToSpt(S,G) {
1167 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1168 * (+) pim_include(S,G) != NULL )
1169 * AND SwitchToSptDesired(S,G) ) {
1170 * # Note: Restarting the KAT will result in the SPT switch
1171 * set KeepaliveTimer(S,G) to Keepalive_Period
1175 * SwitchToSptDesired(S,G) is a policy function that is implementation
1176 * defined. An "infinite threshold" policy can be implemented by making
1177 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1178 * first packet" policy can be implemented by making
1179 * SwitchToSptDesired(S,G) return true once a single packet has been
1180 * received for the source and group.
1182 int pim_upstream_switch_to_spt_desired(struct pim_instance
*pim
,
1183 struct prefix_sg
*sg
)
1185 if (I_am_RP(pim
, sg
->grp
))
1191 int pim_upstream_is_sg_rpt(struct pim_upstream
*up
)
1193 struct listnode
*chnode
;
1194 struct pim_ifchannel
*ch
;
1196 for (ALL_LIST_ELEMENTS_RO(up
->ifchannels
, chnode
, ch
)) {
1197 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
))
1204 * After receiving a packet set SPTbit:
1206 * Update_SPTbit(S,G,iif) {
1207 * if ( iif == RPF_interface(S)
1208 * AND JoinDesired(S,G) == TRUE
1209 * AND ( DirectlyConnected(S) == TRUE
1210 * OR RPF_interface(S) != RPF_interface(RP(G))
1211 * OR inherited_olist(S,G,rpt) == NULL
1212 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1213 * ( RPF'(S,G) != NULL ) )
1214 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1215 * Set SPTbit(S,G) to TRUE
1219 void pim_upstream_set_sptbit(struct pim_upstream
*up
,
1220 struct interface
*incoming
)
1222 struct pim_upstream
*starup
= up
->parent
;
1224 // iif == RPF_interfvace(S)
1225 if (up
->rpf
.source_nexthop
.interface
!= incoming
) {
1226 if (PIM_DEBUG_TRACE
)
1228 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1229 __PRETTY_FUNCTION__
, incoming
->name
,
1230 up
->rpf
.source_nexthop
.interface
->name
);
1234 // AND JoinDesired(S,G) == TRUE
1237 // DirectlyConnected(S) == TRUE
1238 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1240 if (PIM_DEBUG_TRACE
)
1241 zlog_debug("%s: %s is directly connected to the source",
1242 __PRETTY_FUNCTION__
, up
->sg_str
);
1243 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1247 // OR RPF_interface(S) != RPF_interface(RP(G))
1249 || up
->rpf
.source_nexthop
1250 .interface
!= starup
->rpf
.source_nexthop
.interface
) {
1251 if (PIM_DEBUG_TRACE
)
1253 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1254 __PRETTY_FUNCTION__
, up
->sg_str
);
1255 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1259 // OR inherited_olist(S,G,rpt) == NULL
1260 if (pim_upstream_is_sg_rpt(up
)
1261 && pim_upstream_empty_inherited_olist(up
)) {
1262 if (PIM_DEBUG_TRACE
)
1263 zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
1264 __PRETTY_FUNCTION__
, up
->sg_str
);
1265 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1269 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1270 // ( RPF'(S,G) != NULL ) )
1271 if (up
->parent
&& pim_rpf_is_same(&up
->rpf
, &up
->parent
->rpf
)) {
1272 if (PIM_DEBUG_TRACE
)
1273 zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
1274 __PRETTY_FUNCTION__
, up
->sg_str
);
1275 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
1282 const char *pim_upstream_state2str(enum pim_upstream_state join_state
)
1284 switch (join_state
) {
1285 case PIM_UPSTREAM_NOTJOINED
:
1288 case PIM_UPSTREAM_JOINED
:
1295 const char *pim_reg_state2str(enum pim_reg_state reg_state
, char *state_str
)
1297 switch (reg_state
) {
1298 case PIM_REG_NOINFO
:
1299 strcpy(state_str
, "RegNoInfo");
1302 strcpy(state_str
, "RegJoined");
1304 case PIM_REG_JOIN_PENDING
:
1305 strcpy(state_str
, "RegJoinPend");
1308 strcpy(state_str
, "RegPrune");
1311 strcpy(state_str
, "RegUnknown");
1316 static int pim_upstream_register_stop_timer(struct thread
*t
)
1318 struct pim_interface
*pim_ifp
;
1319 struct pim_instance
*pim
;
1320 struct pim_upstream
*up
;
1321 struct pim_rpf
*rpg
;
1324 pim
= up
->channel_oil
->pim
;
1326 if (PIM_DEBUG_TRACE
) {
1327 char state_str
[PIM_REG_STATE_STR_LEN
];
1328 zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s",
1329 __PRETTY_FUNCTION__
, up
->sg_str
, pim
->vrf
->name
,
1330 pim_reg_state2str(up
->reg_state
, state_str
));
1333 switch (up
->reg_state
) {
1334 case PIM_REG_JOIN_PENDING
:
1335 up
->reg_state
= PIM_REG_JOIN
;
1336 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1337 PIM_OIF_FLAG_PROTO_PIM
);
1342 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1344 if (PIM_DEBUG_TRACE
)
1346 "%s: Interface: %s is not configured for pim",
1347 __PRETTY_FUNCTION__
,
1348 up
->rpf
.source_nexthop
.interface
->name
);
1351 up
->reg_state
= PIM_REG_JOIN_PENDING
;
1352 pim_upstream_start_register_stop_timer(up
, 1);
1354 if (((up
->channel_oil
->cc
.lastused
/ 100)
1355 > pim
->keep_alive_time
)
1356 && (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
))) {
1357 if (PIM_DEBUG_TRACE
)
1359 "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
1360 __PRETTY_FUNCTION__
);
1363 rpg
= RP(pim_ifp
->pim
, up
->sg
.grp
);
1365 if (PIM_DEBUG_TRACE
)
1367 "%s: Cannot send register for %s no RPF to the RP",
1368 __PRETTY_FUNCTION__
, up
->sg_str
);
1371 memset(&ip_hdr
, 0, sizeof(struct ip
));
1372 ip_hdr
.ip_p
= PIM_IP_PROTO_PIM
;
1375 ip_hdr
.ip_src
= up
->sg
.src
;
1376 ip_hdr
.ip_dst
= up
->sg
.grp
;
1377 ip_hdr
.ip_len
= htons(20);
1378 // checksum is broken
1379 pim_register_send((uint8_t *)&ip_hdr
, sizeof(struct ip
),
1380 pim_ifp
->primary_address
, rpg
, 1, up
);
1389 void pim_upstream_start_register_stop_timer(struct pim_upstream
*up
,
1394 THREAD_TIMER_OFF(up
->t_rs_timer
);
1396 if (!null_register
) {
1397 uint32_t lower
= (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1398 uint32_t upper
= (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD
);
1399 time
= lower
+ (random() % (upper
- lower
+ 1))
1400 - PIM_REGISTER_PROBE_PERIOD
;
1402 time
= PIM_REGISTER_PROBE_PERIOD
;
1404 if (PIM_DEBUG_TRACE
) {
1406 "%s: (S,G)=%s Starting upstream register stop timer %d",
1407 __PRETTY_FUNCTION__
, up
->sg_str
, time
);
1409 thread_add_timer(router
->master
, pim_upstream_register_stop_timer
, up
,
1410 time
, &up
->t_rs_timer
);
1413 int pim_upstream_inherited_olist_decide(struct pim_instance
*pim
,
1414 struct pim_upstream
*up
)
1416 struct interface
*ifp
;
1417 struct pim_interface
*pim_ifp
= NULL
;
1418 struct pim_ifchannel
*ch
, *starch
;
1419 struct pim_upstream
*starup
= up
->parent
;
1420 int output_intf
= 0;
1422 if (up
->rpf
.source_nexthop
.interface
)
1423 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
1425 if (PIM_DEBUG_TRACE
)
1426 zlog_debug("%s: up %s RPF is not present",
1427 __PRETTY_FUNCTION__
, up
->sg_str
);
1429 if (pim_ifp
&& !up
->channel_oil
)
1430 up
->channel_oil
= pim_channel_oil_add(
1431 pim
, &up
->sg
, pim_ifp
->mroute_vif_index
);
1433 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1437 ch
= pim_ifchannel_find(ifp
, &up
->sg
);
1440 starch
= pim_ifchannel_find(ifp
, &starup
->sg
);
1447 if (pim_upstream_evaluate_join_desired_interface(up
, ch
,
1449 int flag
= PIM_OIF_FLAG_PROTO_PIM
;
1452 flag
= PIM_OIF_FLAG_PROTO_STAR
;
1454 pim_channel_add_oif(up
->channel_oil
, ifp
, flag
);
1463 * For a given upstream, determine the inherited_olist
1466 * inherited_olist(S,G,rpt) =
1467 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1468 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1469 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1471 * inherited_olist(S,G) =
1472 * inherited_olist(S,G,rpt) (+)
1473 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1475 * return 1 if there are any output interfaces
1476 * return 0 if there are not any output interfaces
1478 int pim_upstream_inherited_olist(struct pim_instance
*pim
,
1479 struct pim_upstream
*up
)
1481 int output_intf
= pim_upstream_inherited_olist_decide(pim
, up
);
1484 * If we have output_intf switch state to Join and work like normal
1485 * If we don't have an output_intf that means we are probably a
1486 * switch on a stick so turn on forwarding to just accept the
1487 * incoming packets so we don't bother the other stuff!
1490 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_JOINED
);
1497 int pim_upstream_empty_inherited_olist(struct pim_upstream
*up
)
1499 return pim_channel_oil_empty(up
->channel_oil
);
1503 * When we have a new neighbor,
1504 * find upstreams that don't have their rpf_addr
1505 * set and see if the new neighbor allows
1506 * the join to be sent
1508 void pim_upstream_find_new_rpf(struct pim_instance
*pim
)
1510 struct listnode
*up_node
;
1511 struct listnode
*up_nextnode
;
1512 struct pim_upstream
*up
;
1515 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1517 for (ALL_LIST_ELEMENTS(pim
->upstream_list
, up_node
, up_nextnode
, up
)) {
1518 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
1519 if (PIM_DEBUG_TRACE
)
1521 "Upstream %s without a path to send join, checking",
1523 pim_rpf_update(pim
, up
, NULL
, 1);
1528 unsigned int pim_upstream_hash_key(void *arg
)
1530 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1532 return jhash_2words(up
->sg
.src
.s_addr
, up
->sg
.grp
.s_addr
, 0);
1535 void pim_upstream_terminate(struct pim_instance
*pim
)
1537 struct pim_upstream
*up
;
1539 if (pim
->upstream_list
) {
1540 while (pim
->upstream_list
->count
) {
1541 up
= listnode_head(pim
->upstream_list
);
1542 pim_upstream_del(pim
, up
, __PRETTY_FUNCTION__
);
1545 list_delete(&pim
->upstream_list
);
1548 if (pim
->upstream_hash
)
1549 hash_free(pim
->upstream_hash
);
1550 pim
->upstream_hash
= NULL
;
1552 if (pim
->upstream_sg_wheel
)
1553 wheel_delete(pim
->upstream_sg_wheel
);
1554 pim
->upstream_sg_wheel
= NULL
;
1557 bool pim_upstream_equal(const void *arg1
, const void *arg2
)
1559 const struct pim_upstream
*up1
= (const struct pim_upstream
*)arg1
;
1560 const struct pim_upstream
*up2
= (const struct pim_upstream
*)arg2
;
1562 if ((up1
->sg
.grp
.s_addr
== up2
->sg
.grp
.s_addr
)
1563 && (up1
->sg
.src
.s_addr
== up2
->sg
.src
.s_addr
))
1569 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1570 * the cases where kat has to be restarted on rxing traffic -
1572 * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
1573 * set KeepaliveTimer(S,G) to Keepalive_Period
1574 * # Note: a register state transition or UpstreamJPState(S,G)
1575 * # transition may happen as a result of restarting
1576 * # KeepaliveTimer, and must be dealt with here.
1578 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1579 * inherited_olist(S,G) != NULL ) {
1580 * set KeepaliveTimer(S,G) to Keepalive_Period
1583 static bool pim_upstream_kat_start_ok(struct pim_upstream
*up
)
1585 struct pim_instance
*pim
= up
->channel_oil
->pim
;
1587 /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
1588 * so we will skip that here */
1589 if (pim_if_connected_to_source(up
->rpf
.source_nexthop
.interface
,
1594 if ((up
->join_state
== PIM_UPSTREAM_JOINED
)
1595 && !pim_upstream_empty_inherited_olist(up
)) {
1596 /* XXX: I have added this RP check just for 3.2 and it's a
1598 * what rfc-4601 says. Till now we were only running KAT on FHR
1600 * there is some angst around making the change to run it all
1602 * maintain the (S, G) state. This is tracked via CM-13601 and
1604 * removed to handle spt turn-arounds correctly in a 3-tier clos
1606 if (I_am_RP(pim
, up
->sg
.grp
))
1614 * Code to check and see if we've received packets on a S,G mroute
1615 * and if so to set the SPT bit appropriately
1617 static void pim_upstream_sg_running(void *arg
)
1619 struct pim_upstream
*up
= (struct pim_upstream
*)arg
;
1620 struct pim_instance
*pim
= up
->channel_oil
->pim
;
1622 // No packet can have arrived here if this is the case
1623 if (!up
->channel_oil
->installed
) {
1624 if (PIM_DEBUG_TRACE
)
1625 zlog_debug("%s: %s[%s] is not installed in mroute",
1626 __PRETTY_FUNCTION__
, up
->sg_str
,
1632 * This is a bit of a hack
1633 * We've noted that we should rescan but
1634 * we've missed the window for doing so in
1635 * pim_zebra.c for some reason. I am
1636 * only doing this at this point in time
1637 * to get us up and working for the moment
1639 if (up
->channel_oil
->oil_inherited_rescan
) {
1640 if (PIM_DEBUG_TRACE
)
1642 "%s: Handling unscanned inherited_olist for %s[%s]",
1643 __PRETTY_FUNCTION__
, up
->sg_str
,
1645 pim_upstream_inherited_olist_decide(pim
, up
);
1646 up
->channel_oil
->oil_inherited_rescan
= 0;
1648 pim_mroute_update_counters(up
->channel_oil
);
1650 // Have we seen packets?
1651 if ((up
->channel_oil
->cc
.oldpktcnt
>= up
->channel_oil
->cc
.pktcnt
)
1652 && (up
->channel_oil
->cc
.lastused
/ 100 > 30)) {
1653 if (PIM_DEBUG_TRACE
) {
1655 "%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
1656 __PRETTY_FUNCTION__
, up
->sg_str
, pim
->vrf
->name
,
1657 up
->channel_oil
->cc
.oldpktcnt
,
1658 up
->channel_oil
->cc
.pktcnt
,
1659 up
->channel_oil
->cc
.lastused
/ 100);
1664 if (pim_upstream_kat_start_ok(up
)) {
1665 /* Add a source reference to the stream if
1666 * one doesn't already exist */
1667 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up
->flags
)) {
1668 if (PIM_DEBUG_TRACE
)
1670 "source reference created on kat restart %s[%s]",
1671 up
->sg_str
, pim
->vrf
->name
);
1673 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM
,
1674 __PRETTY_FUNCTION__
);
1675 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
1676 pim_upstream_fhr_kat_start(up
);
1678 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
1679 } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up
->flags
))
1680 pim_upstream_keep_alive_timer_start(up
, pim
->keep_alive_time
);
1682 if (up
->sptbit
!= PIM_UPSTREAM_SPTBIT_TRUE
) {
1683 pim_upstream_set_sptbit(up
, up
->rpf
.source_nexthop
.interface
);
1688 void pim_upstream_add_lhr_star_pimreg(struct pim_instance
*pim
)
1690 struct pim_upstream
*up
;
1691 struct listnode
*node
;
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
))
1700 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1701 PIM_OIF_FLAG_PROTO_IGMP
);
1705 void pim_upstream_spt_prefix_list_update(struct pim_instance
*pim
,
1706 struct prefix_list
*pl
)
1708 const char *pname
= prefix_list_name(pl
);
1710 if (pim
->spt
.plist
&& strcmp(pim
->spt
.plist
, pname
) == 0) {
1711 pim_upstream_remove_lhr_star_pimreg(pim
, pname
);
1716 * nlist -> The new prefix list
1718 * Per Group Application of pimreg to the OIL
1719 * If the prefix list tells us DENY then
1720 * we need to Switchover to SPT immediate
1721 * so add the pimreg.
1722 * If the prefix list tells us to ACCEPT than
1723 * we need to Never do the SPT so remove
1727 void pim_upstream_remove_lhr_star_pimreg(struct pim_instance
*pim
,
1730 struct pim_upstream
*up
;
1731 struct listnode
*node
;
1732 struct prefix_list
*np
;
1734 enum prefix_list_type apply_new
;
1736 np
= prefix_list_lookup(AFI_IP
, nlist
);
1739 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1741 for (ALL_LIST_ELEMENTS_RO(pim
->upstream_list
, node
, up
)) {
1742 if (up
->sg
.src
.s_addr
!= INADDR_ANY
)
1745 if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
))
1749 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
1750 PIM_OIF_FLAG_PROTO_IGMP
);
1753 g
.u
.prefix4
= up
->sg
.grp
;
1754 apply_new
= prefix_list_apply(np
, &g
);
1755 if (apply_new
== PREFIX_DENY
)
1756 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1757 PIM_OIF_FLAG_PROTO_IGMP
);
1759 pim_channel_del_oif(up
->channel_oil
, pim
->regiface
,
1760 PIM_OIF_FLAG_PROTO_IGMP
);
1764 void pim_upstream_init(struct pim_instance
*pim
)
1768 snprintf(name
, 64, "PIM %s Timer Wheel",
1770 pim
->upstream_sg_wheel
=
1771 wheel_init(router
->master
, 31000, 100, pim_upstream_hash_key
,
1772 pim_upstream_sg_running
, name
);
1774 snprintf(name
, 64, "PIM %s Upstream Hash",
1776 pim
->upstream_hash
= hash_create_size(8192, pim_upstream_hash_key
,
1777 pim_upstream_equal
, name
);
1779 pim
->upstream_list
= list_new();
1780 pim
->upstream_list
->cmp
= pim_upstream_compare
;