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
33 #include "pim_iface.h"
34 #include "pim_ifchannel.h"
35 #include "pim_zebra.h"
41 #include "pim_macro.h"
43 #include "pim_upstream.h"
46 RB_GENERATE(pim_ifchannel_rb
, pim_ifchannel
,
47 pim_ifp_rb
, pim_ifchannel_compare
);
49 int pim_ifchannel_compare(const struct pim_ifchannel
*ch1
,
50 const struct pim_ifchannel
*ch2
)
52 struct pim_interface
*pim_ifp1
;
53 struct pim_interface
*pim_ifp2
;
55 pim_ifp1
= ch1
->interface
->info
;
56 pim_ifp2
= ch2
->interface
->info
;
58 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
61 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
64 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
67 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
70 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
73 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
80 * A (*,G) or a (*,*) is going away
81 * remove the parent pointer from
82 * those pointing at us
84 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
86 struct pim_ifchannel
*child
;
91 while (!list_isempty(ch
->sources
)) {
92 child
= listnode_head(ch
->sources
);
94 listnode_delete(ch
->sources
, child
);
99 * A (*,G) or a (*,*) is being created
100 * find all the children that would point
103 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
105 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
106 struct pim_ifchannel
*child
;
108 // Basic Sanity that we are not being silly
109 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
)
110 && (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
113 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
)
114 && (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
117 RB_FOREACH(child
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
) {
118 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
)
119 && (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
)
122 listnode_add_sort(ch
->sources
, child
);
127 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
129 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
132 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
134 struct pim_interface
*pim_ifp
;
136 pim_ifp
= ch
->interface
->info
;
138 if (ch
->upstream
->channel_oil
) {
139 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
140 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
141 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
143 /* SGRpt entry could have empty oil */
144 if (ch
->upstream
->channel_oil
)
145 pim_channel_del_oif(ch
->upstream
->channel_oil
,
146 ch
->interface
, mask
);
148 * Do we have any S,G's that are inheriting?
149 * Nuke from on high too.
151 if (ch
->upstream
->sources
) {
152 struct pim_upstream
*child
;
153 struct listnode
*up_node
;
155 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
157 pim_channel_del_oif(child
->channel_oil
,
159 PIM_OIF_FLAG_PROTO_STAR
);
164 * When this channel is removed
165 * we need to find all our children
166 * and make sure our pointers are fixed
168 pim_ifchannel_remove_children(ch
);
171 list_delete(ch
->sources
);
173 listnode_delete(ch
->upstream
->ifchannels
, ch
);
175 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
176 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
179 /* upstream is common across ifchannels, check if upstream's
180 ifchannel list is empty before deleting upstream_del
181 ref count will take care of it.
183 pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __PRETTY_FUNCTION__
);
186 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
187 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
188 THREAD_OFF(ch
->t_ifassert_timer
);
191 listnode_delete(ch
->parent
->sources
, ch
);
195 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
197 if (PIM_DEBUG_PIM_TRACE
)
198 zlog_debug("%s: ifchannel entry %s is deleted ",
199 __PRETTY_FUNCTION__
, ch
->sg_str
);
201 pim_ifchannel_free(ch
);
204 void pim_ifchannel_delete_all(struct interface
*ifp
)
206 struct pim_interface
*pim_ifp
;
207 struct pim_ifchannel
*ch
;
213 while ((ch
= RB_ROOT(pim_ifchannel_rb
,
214 &pim_ifp
->ifchannel_rb
)) != NULL
) {
215 pim_ifchannel_delete(ch
);
219 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
221 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
222 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
223 && ch
->t_ifjoin_expiry_timer
== NULL
)
224 pim_ifchannel_delete(ch
);
227 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
228 enum pim_ifjoin_state new_state
)
230 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
231 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
233 if (PIM_DEBUG_PIM_EVENTS
)
235 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
236 ch
->interface
->name
, ch
->sg_str
,
237 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
238 pim_ifchannel_ifjoin_name(new_state
, 0));
241 if (old_state
== new_state
) {
242 if (PIM_DEBUG_PIM_EVENTS
) {
244 "%s calledby %s: non-transition on state %d (%s)",
245 __PRETTY_FUNCTION__
, caller
, new_state
,
246 pim_ifchannel_ifjoin_name(new_state
, 0));
251 ch
->ifjoin_state
= new_state
;
253 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
254 struct pim_upstream
*up
= ch
->upstream
;
255 struct pim_upstream
*child
;
256 struct listnode
*up_node
;
259 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
260 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
262 struct channel_oil
*c_oil
=
265 if (PIM_DEBUG_PIM_TRACE
)
267 "%s %s: Prune(S,G)=%s from %s",
275 if (!pim_upstream_evaluate_join_desired(
276 pim_ifp
->pim
, child
)) {
278 c_oil
, ch
->interface
,
279 PIM_OIF_FLAG_PROTO_STAR
);
280 pim_upstream_update_join_desired(
281 pim_ifp
->pim
, child
);
285 * If the S,G has no if channel and the
287 * has output here then the *,G was
288 * supplying the implied
289 * if channel. So remove it.
290 * I think this is dead code now. is it?
292 if (c_oil
->oil
.mfcc_ttls
293 [pim_ifp
->mroute_vif_index
])
295 c_oil
, ch
->interface
,
296 PIM_OIF_FLAG_PROTO_STAR
);
299 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
300 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
302 if (PIM_DEBUG_PIM_TRACE
)
304 "%s %s: Join(S,G)=%s from %s",
310 if (pim_upstream_evaluate_join_desired(
311 pim_ifp
->pim
, child
)) {
315 PIM_OIF_FLAG_PROTO_STAR
);
316 pim_upstream_update_join_desired(
317 pim_ifp
->pim
, child
);
323 /* Transition to/from NOINFO ? */
324 if ((old_state
== PIM_IFJOIN_NOINFO
)
325 || (new_state
== PIM_IFJOIN_NOINFO
)) {
327 if (PIM_DEBUG_PIM_EVENTS
) {
328 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
329 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
331 ch
->sg_str
, ch
->interface
->name
);
335 Record uptime of state transition to/from NOINFO
337 ch
->ifjoin_creation
= pim_time_monotonic_sec();
339 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
340 pim_ifchannel_update_could_assert(ch
);
341 pim_ifchannel_update_assert_tracking_desired(ch
);
345 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
348 switch (ifjoin_state
) {
349 case PIM_IFJOIN_NOINFO
:
350 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
355 case PIM_IFJOIN_JOIN
:
358 case PIM_IFJOIN_PRUNE
:
361 case PIM_IFJOIN_PRUNE_PENDING
:
364 case PIM_IFJOIN_PRUNE_TMP
:
367 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
372 return "ifjoin_bad_state";
375 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
377 switch (ifassert_state
) {
378 case PIM_IFASSERT_NOINFO
:
380 case PIM_IFASSERT_I_AM_WINNER
:
382 case PIM_IFASSERT_I_AM_LOSER
:
386 return "ifassert_bad_state";
390 RFC 4601: 4.6.5. Assert State Macros
392 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
393 defaults to Infinity when in the NoInfo state.
395 void reset_ifassert_state(struct pim_ifchannel
*ch
)
397 struct in_addr any
= {.s_addr
= INADDR_ANY
};
399 THREAD_OFF(ch
->t_ifassert_timer
);
401 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
402 qpim_infinite_assert_metric
);
405 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
406 struct prefix_sg
*sg
)
408 struct pim_interface
*pim_ifp
;
409 struct pim_ifchannel
*ch
;
410 struct pim_ifchannel lookup
;
415 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
416 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
421 lookup
.interface
= ifp
;
422 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
427 static void ifmembership_set(struct pim_ifchannel
*ch
,
428 enum pim_ifmembership membership
)
430 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
432 if (ch
->local_ifmembership
== membership
)
435 if (PIM_DEBUG_PIM_EVENTS
) {
436 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
437 __PRETTY_FUNCTION__
, ch
->sg_str
,
438 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
440 ch
->interface
->name
);
443 ch
->local_ifmembership
= membership
;
445 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
446 pim_ifchannel_update_could_assert(ch
);
447 pim_ifchannel_update_assert_tracking_desired(ch
);
451 void pim_ifchannel_membership_clear(struct interface
*ifp
)
453 struct pim_interface
*pim_ifp
;
454 struct pim_ifchannel
*ch
;
459 RB_FOREACH(ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
460 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
463 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
465 struct pim_interface
*pim_ifp
;
466 struct pim_ifchannel
*ch
, *ch_tmp
;
471 RB_FOREACH_SAFE(ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
472 delete_on_noinfo(ch
);
476 * For a given Interface, if we are given a S,G
477 * Find the *,G (If we have it).
478 * If we are passed a *,G, find the *,* ifchannel
481 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
483 struct prefix_sg parent_sg
= ch
->sg
;
484 struct pim_ifchannel
*parent
= NULL
;
487 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
488 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
489 parent_sg
.src
.s_addr
= INADDR_ANY
;
490 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
493 listnode_add(parent
->sources
, ch
);
500 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
501 struct prefix_sg
*sg
,
502 uint8_t source_flags
, int up_flags
)
504 struct pim_interface
*pim_ifp
;
505 struct pim_ifchannel
*ch
;
506 struct pim_upstream
*up
;
508 ch
= pim_ifchannel_find(ifp
, sg
);
514 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
517 "%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
518 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
523 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
524 && !(source_flags
& PIM_ENCODE_WC_BIT
))
525 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
529 pim_str_sg_set(sg
, ch
->sg_str
);
530 ch
->parent
= pim_ifchannel_find_parent(ch
);
531 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
532 ch
->sources
= list_new();
534 (int (*)(void *, void *))pim_ifchannel_compare
;
538 pim_ifchannel_find_new_children(ch
);
539 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
541 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
542 ch
->t_ifjoin_expiry_timer
= NULL
;
543 ch
->t_ifjoin_prune_pending_timer
= NULL
;
544 ch
->ifjoin_creation
= 0;
546 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
548 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
,
549 __PRETTY_FUNCTION__
, ch
);
553 "%s: could not attach upstream (S,G)=%s on interface %s",
554 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
556 pim_ifchannel_remove_children(ch
);
558 list_delete(ch
->sources
);
560 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
561 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
562 THREAD_OFF(ch
->t_ifassert_timer
);
564 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
565 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
570 listnode_add_sort(up
->ifchannels
, ch
);
572 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
573 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
575 ch
->ifassert_winner
.s_addr
= 0;
578 ch
->t_ifassert_timer
= NULL
;
579 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
580 reset_ifassert_state(ch
);
581 if (pim_macro_ch_could_assert_eval(ch
))
582 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
584 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
586 if (pim_macro_assert_tracking_desired_eval(ch
))
587 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
589 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
591 if (PIM_DEBUG_PIM_TRACE
)
592 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
598 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
600 pim_forward_stop(ch
, !ch_del
);
601 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
603 delete_on_noinfo(ch
);
606 static int on_ifjoin_expiry_timer(struct thread
*t
)
608 struct pim_ifchannel
*ch
;
612 ifjoin_to_noinfo(ch
, true);
613 /* ch may have been deleted */
618 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
620 struct pim_ifchannel
*ch
;
621 int send_prune_echo
; /* boolean */
622 struct interface
*ifp
;
623 struct pim_interface
*pim_ifp
;
627 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
628 /* Send PruneEcho(S,G) ? */
631 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
633 if (send_prune_echo
) {
636 rpf
.source_nexthop
.interface
= ifp
;
637 rpf
.rpf_addr
.u
.prefix4
= pim_ifp
->primary_address
;
638 pim_jp_agg_single_upstream_send(&rpf
, ch
->upstream
, 0);
640 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
641 message on RP path upon prune timer expiry.
643 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
645 pim_upstream_update_join_desired(pim_ifp
->pim
,
648 ch->ifjoin_state transition to NOINFO state
649 ch_del is set to 0 for not deleteing from here.
650 Holdtime expiry (ch_del set to 1) delete the entry.
652 ifjoin_to_noinfo(ch
, false);
654 ifjoin_to_noinfo(ch
, true);
655 /* from here ch may have been deleted */
658 "%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
659 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
660 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
666 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
667 struct in_addr upstream
, struct prefix_sg
*sg
,
668 uint8_t source_flags
, int holdtime
)
670 struct pim_upstream
*up
;
671 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
673 /* Upstream (S,G) in Joined state ? */
674 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
677 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
680 /* Upstream (S,G) in Joined state */
682 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
683 /* RPF'(S,G) not found */
684 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
685 __PRETTY_FUNCTION__
, up
->sg_str
);
689 /* upstream directed to RPF'(S,G) ? */
690 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
691 char up_str
[INET_ADDRSTRLEN
];
692 char rpf_str
[PREFIX_STRLEN
];
693 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
694 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
697 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
698 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
699 rpf_str
, recv_ifp
->name
);
702 /* upstream directed to RPF'(S,G) */
705 /* Join(S,G) to RPF'(S,G) */
706 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
711 /* Prune to RPF'(S,G) */
713 if (source_flags
& PIM_RPT_BIT_MASK
) {
714 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
715 /* Prune(*,G) to RPF'(S,G) */
716 pim_upstream_join_timer_decrease_to_t_override(
721 /* Prune(S,G,rpt) to RPF'(S,G) */
722 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
727 /* Prune(S,G) to RPF'(S,G) */
728 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
731 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
732 struct in_addr upstream
, struct prefix_sg
*sg
,
733 uint8_t source_flags
, uint16_t holdtime
)
735 struct pim_interface
*recv_pim_ifp
;
736 int is_local
; /* boolean */
738 recv_pim_ifp
= recv_ifp
->info
;
739 zassert(recv_pim_ifp
);
741 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
746 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
747 char up_str
[INET_ADDRSTRLEN
];
748 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
749 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
750 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
751 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
755 * Since recv upstream addr was not directed to our primary
756 * address, check if we should react to it in any way.
758 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
761 return 1; /* non-local */
764 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
765 struct in_addr upstream
, struct prefix_sg
*sg
,
766 uint8_t source_flags
, uint16_t holdtime
)
768 struct pim_interface
*pim_ifp
;
769 struct pim_ifchannel
*ch
;
771 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
776 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
777 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
782 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
784 Transitions from "I am Assert Loser" State
786 Receive Join(S,G) on Interface I
788 We receive a Join(S,G) that has the Upstream Neighbor Address
789 field set to my primary IP address on interface I. The action is
790 to transition to NoInfo state, delete this (S,G) assert state
791 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
794 Notice: The nonlocal_upstream() test above ensures the upstream
795 address of the join message is our primary address.
797 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
798 char neigh_str
[INET_ADDRSTRLEN
];
799 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
801 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
802 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
805 assert_action_a5(ch
);
811 switch (ch
->ifjoin_state
) {
812 case PIM_IFJOIN_NOINFO
:
813 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
815 if (pim_macro_chisin_oiflist(ch
)) {
816 pim_upstream_inherited_olist(pim_ifp
->pim
,
818 pim_forward_start(ch
);
821 * If we are going to be a LHR, we need to note it
823 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
824 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
825 && !(ch
->upstream
->flags
826 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
827 pim_upstream_ref(ch
->upstream
,
828 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
829 __PRETTY_FUNCTION__
);
830 pim_upstream_keep_alive_timer_start(
831 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
834 case PIM_IFJOIN_JOIN
:
835 zassert(!ch
->t_ifjoin_prune_pending_timer
);
838 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
840 previously received join message with holdtime=0xFFFF.
842 if (ch
->t_ifjoin_expiry_timer
) {
843 unsigned long remain
= thread_timer_remain_second(
844 ch
->t_ifjoin_expiry_timer
);
845 if (remain
> holdtime
) {
847 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
850 Transitions from Join State
852 The (S,G) downstream state machine on
853 interface I remains in
854 Join state, and the Expiry Timer (ET) is
856 maximum of its current value and the HoldTime
858 triggering Join/Prune message.
860 Conclusion: Do not change the ET if the
862 higher than the received join holdtime.
867 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
869 case PIM_IFJOIN_PRUNE
:
870 if (source_flags
& PIM_ENCODE_RPT_BIT
)
871 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
874 case PIM_IFJOIN_PRUNE_PENDING
:
875 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
876 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
877 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
878 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
881 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
884 case PIM_IFJOIN_PRUNE_TMP
:
886 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
890 if (holdtime
!= 0xFFFF) {
891 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
892 &ch
->t_ifjoin_expiry_timer
);
896 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
897 struct prefix_sg
*sg
, uint8_t source_flags
,
900 struct pim_ifchannel
*ch
;
901 struct pim_interface
*pim_ifp
;
902 int jp_override_interval_msec
;
904 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
909 ch
= pim_ifchannel_find(ifp
, sg
);
910 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
913 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
914 __PRETTY_FUNCTION__
, ifp
->name
,
915 pim_str_sg_dump(sg
), source_flags
);
919 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
920 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
926 switch (ch
->ifjoin_state
) {
927 case PIM_IFJOIN_NOINFO
:
928 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
929 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
930 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
932 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
933 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
934 jp_override_interval_msec
=
935 pim_if_jp_override_interval_msec(ifp
);
937 jp_override_interval_msec
=
938 0; /* schedule to expire immediately */
939 /* If we called ifjoin_prune() directly instead, care
941 be taken not to use "ch" afterwards since it would be
944 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
945 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
946 thread_add_timer_msec(
947 master
, on_ifjoin_prune_pending_timer
, ch
,
948 jp_override_interval_msec
,
949 &ch
->t_ifjoin_prune_pending_timer
);
950 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
951 holdtime
, &ch
->t_ifjoin_expiry_timer
);
952 pim_upstream_update_join_desired(pim_ifp
->pim
,
956 case PIM_IFJOIN_PRUNE_PENDING
:
959 case PIM_IFJOIN_JOIN
:
960 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
962 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
963 PIM_IFJOIN_PRUNE_PENDING
);
965 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
966 jp_override_interval_msec
=
967 pim_if_jp_override_interval_msec(ifp
);
969 jp_override_interval_msec
=
970 0; /* schedule to expire immediately */
971 /* If we called ifjoin_prune() directly instead, care should
972 be taken not to use "ch" afterwards since it would be
974 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
975 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
976 jp_override_interval_msec
,
977 &ch
->t_ifjoin_prune_pending_timer
);
979 case PIM_IFJOIN_PRUNE
:
980 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
981 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
982 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
983 holdtime
, &ch
->t_ifjoin_expiry_timer
);
986 case PIM_IFJOIN_PRUNE_TMP
:
987 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
988 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
989 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
990 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
991 holdtime
, &ch
->t_ifjoin_expiry_timer
);
994 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
995 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
996 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
997 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
998 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
999 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1005 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1006 struct prefix_sg
*sg
)
1008 struct pim_ifchannel
*ch
, *starch
;
1009 struct pim_interface
*pim_ifp
;
1010 struct pim_instance
*pim
;
1012 /* PIM enabled on interface? */
1013 pim_ifp
= ifp
->info
;
1016 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1021 /* skip (*,G) ch creation if G is of type SSM */
1022 if (sg
->src
.s_addr
== INADDR_ANY
) {
1023 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1024 if (PIM_DEBUG_PIM_EVENTS
)
1026 "%s: local membership (S,G)=%s ignored as group is SSM",
1027 __PRETTY_FUNCTION__
,
1028 pim_str_sg_dump(sg
));
1033 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1038 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1040 if (sg
->src
.s_addr
== INADDR_ANY
) {
1041 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1042 struct pim_upstream
*child
;
1043 struct listnode
*up_node
;
1047 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1048 if (PIM_DEBUG_EVENTS
)
1049 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1050 __FILE__
, __PRETTY_FUNCTION__
,
1051 child
->sg_str
, ifp
->name
,
1054 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1055 if (pim_upstream_evaluate_join_desired_interface(
1056 child
, ch
, starch
)) {
1057 pim_channel_add_oif(child
->channel_oil
, ifp
,
1058 PIM_OIF_FLAG_PROTO_STAR
);
1059 pim_upstream_switch(pim
, child
,
1060 PIM_UPSTREAM_JOINED
);
1064 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1065 if (pim
->spt
.plist
) {
1066 struct prefix_list
*plist
= prefix_list_lookup(
1067 AFI_IP
, pim
->spt
.plist
);
1070 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1071 g
.u
.prefix4
= up
->sg
.grp
;
1073 if (prefix_list_apply(plist
, &g
)
1075 pim_channel_add_oif(
1076 up
->channel_oil
, pim
->regiface
,
1077 PIM_OIF_FLAG_PROTO_IGMP
);
1081 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1082 PIM_OIF_FLAG_PROTO_IGMP
);
1088 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1089 struct prefix_sg
*sg
)
1091 struct pim_ifchannel
*starch
, *ch
, *orig
;
1092 struct pim_interface
*pim_ifp
;
1094 /* PIM enabled on interface? */
1095 pim_ifp
= ifp
->info
;
1098 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1101 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1105 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1107 if (sg
->src
.s_addr
== INADDR_ANY
) {
1108 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1109 struct pim_upstream
*child
;
1110 struct listnode
*up_node
, *up_nnode
;
1114 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1115 struct channel_oil
*c_oil
= child
->channel_oil
;
1116 struct pim_ifchannel
*chchannel
=
1117 pim_ifchannel_find(ifp
, &child
->sg
);
1118 struct pim_interface
*pim_ifp
= ifp
->info
;
1120 if (PIM_DEBUG_EVENTS
)
1121 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1122 __FILE__
, __PRETTY_FUNCTION__
,
1123 up
->sg_str
, ifp
->name
,
1126 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1128 && !pim_upstream_evaluate_join_desired_interface(
1130 pim_channel_del_oif(c_oil
, ifp
,
1131 PIM_OIF_FLAG_PROTO_STAR
);
1134 * If the S,G has no if channel and the c_oil still
1135 * has output here then the *,G was supplying the
1137 * if channel. So remove it.
1139 if (!chchannel
&& c_oil
1140 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1141 pim_channel_del_oif(c_oil
, ifp
,
1142 PIM_OIF_FLAG_PROTO_STAR
);
1144 /* Child node removal/ref count-- will happen as part of
1145 * parent' delete_no_info */
1148 delete_on_noinfo(orig
);
1151 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1153 int old_couldassert
=
1154 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1155 int new_couldassert
=
1156 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1158 if (new_couldassert
== old_couldassert
)
1161 if (PIM_DEBUG_PIM_EVENTS
) {
1162 char src_str
[INET_ADDRSTRLEN
];
1163 char grp_str
[INET_ADDRSTRLEN
];
1164 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1165 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1166 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1167 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1168 ch
->interface
->name
, old_couldassert
,
1172 if (new_couldassert
) {
1173 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1174 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1176 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1177 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1179 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1180 assert_action_a4(ch
);
1184 pim_ifchannel_update_my_assert_metric(ch
);
1188 my_assert_metric may be affected by:
1191 pim_ifp->primary_address
1192 rpf->source_nexthop.mrib_metric_preference;
1193 rpf->source_nexthop.mrib_route_metric;
1195 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1197 struct pim_assert_metric my_metric_new
=
1198 pim_macro_ch_my_assert_metric_eval(ch
);
1200 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1203 if (PIM_DEBUG_PIM_EVENTS
) {
1204 char src_str
[INET_ADDRSTRLEN
];
1205 char grp_str
[INET_ADDRSTRLEN
];
1206 char old_addr_str
[INET_ADDRSTRLEN
];
1207 char new_addr_str
[INET_ADDRSTRLEN
];
1208 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1209 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1210 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1211 old_addr_str
, sizeof(old_addr_str
));
1212 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1213 new_addr_str
, sizeof(new_addr_str
));
1215 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1216 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1217 ch
->interface
->name
,
1218 ch
->ifassert_my_metric
.rpt_bit_flag
,
1219 ch
->ifassert_my_metric
.metric_preference
,
1220 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1221 my_metric_new
.rpt_bit_flag
,
1222 my_metric_new
.metric_preference
,
1223 my_metric_new
.route_metric
, new_addr_str
);
1226 ch
->ifassert_my_metric
= my_metric_new
;
1228 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1229 &ch
->ifassert_winner_metric
)) {
1230 assert_action_a5(ch
);
1234 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1236 int old_atd
= PIM_FORCE_BOOLEAN(
1237 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1239 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1241 if (new_atd
== old_atd
)
1244 if (PIM_DEBUG_PIM_EVENTS
) {
1245 char src_str
[INET_ADDRSTRLEN
];
1246 char grp_str
[INET_ADDRSTRLEN
];
1247 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1248 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1250 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1251 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1252 ch
->interface
->name
, old_atd
, new_atd
);
1256 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1257 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1259 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1260 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1262 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1263 assert_action_a5(ch
);
1269 * If we have a new pim interface, check to
1270 * see if any of the pre-existing channels have
1271 * their upstream out that way and turn on forwarding
1272 * for that ifchannel then.
1274 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1276 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1277 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1278 struct listnode
*ifnode
;
1279 struct interface
*ifp
;
1281 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(pim
->vrf_id
), ifnode
, ifp
)) {
1282 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1283 struct pim_ifchannel
*ch
;
1288 if (new_pim_ifp
== loop_pim_ifp
)
1291 RB_FOREACH(ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1292 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1293 struct pim_upstream
*up
= ch
->upstream
;
1294 if ((!up
->channel_oil
)
1295 && (up
->rpf
.source_nexthop
1296 .interface
== new_ifp
))
1297 pim_forward_start(ch
);
1304 * Downstream per-interface (S,G,rpt) state machine
1305 * states that we need to move (S,G,rpt) items
1306 * into different states at the start of the
1307 * reception of a *,G join as well, when
1308 * we get End of Message
1310 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1311 uint8_t source_flags
, uint8_t join
,
1312 uint8_t starg_alone
)
1314 struct pim_ifchannel
*child
;
1315 struct listnode
*ch_node
;
1317 if (PIM_DEBUG_PIM_TRACE
)
1319 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1320 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1321 ch
->sg_str
, eom
, join
);
1325 for (ALL_LIST_ELEMENTS_RO(ch
->sources
, ch_node
, child
)) {
1326 /* Only *,G Join received and no (SG-RPT) prune.
1327 eom = 1, only (W,G) join_alone is true, WC and RPT are set.
1328 Scan all S,G associated to G and if any SG-RPT
1329 remove the SG-RPT flag.
1331 if (eom
&& starg_alone
&& (source_flags
& PIM_RPT_BIT_MASK
)
1332 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
1333 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
1334 struct pim_upstream
*up
= child
->upstream
;
1336 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1338 if (PIM_DEBUG_TRACE
)
1340 "%s: SGRpt flag is cleared, add inherit oif to up %s",
1341 __PRETTY_FUNCTION__
,
1343 pim_channel_add_oif(
1344 up
->channel_oil
, ch
->interface
,
1345 PIM_OIF_FLAG_PROTO_STAR
);
1346 pim_ifchannel_ifjoin_switch(
1347 __PRETTY_FUNCTION__
, child
,
1353 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1356 switch (child
->ifjoin_state
) {
1357 case PIM_IFJOIN_NOINFO
:
1358 case PIM_IFJOIN_JOIN
:
1360 case PIM_IFJOIN_PRUNE
:
1362 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1364 case PIM_IFJOIN_PRUNE_PENDING
:
1366 child
->ifjoin_state
=
1367 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1369 case PIM_IFJOIN_PRUNE_TMP
:
1370 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1372 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1378 unsigned int pim_ifchannel_hash_key(void *arg
)
1380 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1382 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);