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"
47 RB_GENERATE(pim_ifchannel_rb
, pim_ifchannel
, 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_and_null(&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 (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
214 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
216 pim_ifchannel_delete(ch
);
220 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
222 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
223 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
224 && ch
->t_ifjoin_expiry_timer
== NULL
)
225 pim_ifchannel_delete(ch
);
228 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
229 enum pim_ifjoin_state new_state
)
231 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
232 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
234 if (PIM_DEBUG_PIM_EVENTS
)
236 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
237 ch
->interface
->name
, ch
->sg_str
,
238 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
239 pim_ifchannel_ifjoin_name(new_state
, 0));
242 if (old_state
== new_state
) {
243 if (PIM_DEBUG_PIM_EVENTS
) {
245 "%s calledby %s: non-transition on state %d (%s)",
246 __PRETTY_FUNCTION__
, caller
, new_state
,
247 pim_ifchannel_ifjoin_name(new_state
, 0));
252 ch
->ifjoin_state
= new_state
;
254 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
255 struct pim_upstream
*up
= ch
->upstream
;
256 struct pim_upstream
*child
;
257 struct listnode
*up_node
;
260 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
261 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
263 struct channel_oil
*c_oil
=
266 if (PIM_DEBUG_PIM_TRACE
)
268 "%s %s: Prune(S,G)=%s from %s",
276 if (!pim_upstream_evaluate_join_desired(
277 pim_ifp
->pim
, child
)) {
279 c_oil
, ch
->interface
,
280 PIM_OIF_FLAG_PROTO_STAR
);
281 pim_upstream_update_join_desired(
282 pim_ifp
->pim
, child
);
286 * If the S,G has no if channel and the
288 * has output here then the *,G was
289 * supplying the implied
290 * if channel. So remove it.
291 * I think this is dead code now. is it?
293 if (c_oil
->oil
.mfcc_ttls
294 [pim_ifp
->mroute_vif_index
])
296 c_oil
, ch
->interface
,
297 PIM_OIF_FLAG_PROTO_STAR
);
300 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
301 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
303 if (PIM_DEBUG_PIM_TRACE
)
305 "%s %s: Join(S,G)=%s from %s",
311 if (pim_upstream_evaluate_join_desired(
312 pim_ifp
->pim
, child
)) {
316 PIM_OIF_FLAG_PROTO_STAR
);
317 pim_upstream_update_join_desired(
318 pim_ifp
->pim
, child
);
324 /* Transition to/from NOINFO ? */
325 if ((old_state
== PIM_IFJOIN_NOINFO
)
326 || (new_state
== PIM_IFJOIN_NOINFO
)) {
328 if (PIM_DEBUG_PIM_EVENTS
) {
329 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
330 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
332 ch
->sg_str
, ch
->interface
->name
);
336 Record uptime of state transition to/from NOINFO
338 ch
->ifjoin_creation
= pim_time_monotonic_sec();
340 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
341 pim_ifchannel_update_could_assert(ch
);
342 pim_ifchannel_update_assert_tracking_desired(ch
);
346 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
349 switch (ifjoin_state
) {
350 case PIM_IFJOIN_NOINFO
:
351 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
356 case PIM_IFJOIN_JOIN
:
359 case PIM_IFJOIN_PRUNE
:
360 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
365 case PIM_IFJOIN_PRUNE_PENDING
:
366 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
371 case PIM_IFJOIN_PRUNE_TMP
:
372 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
377 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
378 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
385 return "ifjoin_bad_state";
388 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
390 switch (ifassert_state
) {
391 case PIM_IFASSERT_NOINFO
:
393 case PIM_IFASSERT_I_AM_WINNER
:
395 case PIM_IFASSERT_I_AM_LOSER
:
399 return "ifassert_bad_state";
403 RFC 4601: 4.6.5. Assert State Macros
405 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
406 defaults to Infinity when in the NoInfo state.
408 void reset_ifassert_state(struct pim_ifchannel
*ch
)
410 struct in_addr any
= {.s_addr
= INADDR_ANY
};
412 THREAD_OFF(ch
->t_ifassert_timer
);
414 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
415 qpim_infinite_assert_metric
);
418 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
419 struct prefix_sg
*sg
)
421 struct pim_interface
*pim_ifp
;
422 struct pim_ifchannel
*ch
;
423 struct pim_ifchannel lookup
;
428 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
429 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
434 lookup
.interface
= ifp
;
435 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
440 static void ifmembership_set(struct pim_ifchannel
*ch
,
441 enum pim_ifmembership membership
)
443 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
445 if (ch
->local_ifmembership
== membership
)
448 if (PIM_DEBUG_PIM_EVENTS
) {
449 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
450 __PRETTY_FUNCTION__
, ch
->sg_str
,
451 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
453 ch
->interface
->name
);
456 ch
->local_ifmembership
= membership
;
458 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
459 pim_ifchannel_update_could_assert(ch
);
460 pim_ifchannel_update_assert_tracking_desired(ch
);
464 void pim_ifchannel_membership_clear(struct interface
*ifp
)
466 struct pim_interface
*pim_ifp
;
467 struct pim_ifchannel
*ch
;
472 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
473 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
476 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
478 struct pim_interface
*pim_ifp
;
479 struct pim_ifchannel
*ch
, *ch_tmp
;
484 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
485 delete_on_noinfo(ch
);
489 * For a given Interface, if we are given a S,G
490 * Find the *,G (If we have it).
491 * If we are passed a *,G, find the *,* ifchannel
494 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
496 struct prefix_sg parent_sg
= ch
->sg
;
497 struct pim_ifchannel
*parent
= NULL
;
500 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
501 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
502 parent_sg
.src
.s_addr
= INADDR_ANY
;
503 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
506 listnode_add(parent
->sources
, ch
);
513 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
514 struct prefix_sg
*sg
,
515 uint8_t source_flags
, int up_flags
)
517 struct pim_interface
*pim_ifp
;
518 struct pim_ifchannel
*ch
;
519 struct pim_upstream
*up
;
521 ch
= pim_ifchannel_find(ifp
, sg
);
527 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
530 "%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
531 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
536 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
537 && !(source_flags
& PIM_ENCODE_WC_BIT
))
538 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
542 pim_str_sg_set(sg
, ch
->sg_str
);
543 ch
->parent
= pim_ifchannel_find_parent(ch
);
544 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
545 ch
->sources
= list_new();
547 (int (*)(void *, void *))pim_ifchannel_compare
;
551 pim_ifchannel_find_new_children(ch
);
552 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
554 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
555 ch
->t_ifjoin_expiry_timer
= NULL
;
556 ch
->t_ifjoin_prune_pending_timer
= NULL
;
557 ch
->ifjoin_creation
= 0;
559 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
561 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
,
562 __PRETTY_FUNCTION__
, ch
);
566 "%s: could not attach upstream (S,G)=%s on interface %s",
567 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
570 listnode_delete(ch
->parent
->sources
, ch
);
572 pim_ifchannel_remove_children(ch
);
574 list_delete_and_null(&ch
->sources
);
576 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
577 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
578 THREAD_OFF(ch
->t_ifassert_timer
);
580 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
581 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
586 listnode_add_sort(up
->ifchannels
, ch
);
588 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
589 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
591 ch
->ifassert_winner
.s_addr
= 0;
594 ch
->t_ifassert_timer
= NULL
;
595 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
596 reset_ifassert_state(ch
);
597 if (pim_macro_ch_could_assert_eval(ch
))
598 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
600 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
602 if (pim_macro_assert_tracking_desired_eval(ch
))
603 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
605 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
607 if (PIM_DEBUG_PIM_TRACE
)
608 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
614 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
616 pim_forward_stop(ch
, !ch_del
);
617 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
619 delete_on_noinfo(ch
);
622 static int on_ifjoin_expiry_timer(struct thread
*t
)
624 struct pim_ifchannel
*ch
;
628 ifjoin_to_noinfo(ch
, true);
629 /* ch may have been deleted */
634 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
636 struct pim_ifchannel
*ch
;
637 int send_prune_echo
; /* boolean */
638 struct interface
*ifp
;
639 struct pim_interface
*pim_ifp
;
645 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
646 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
647 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
649 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
652 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
653 /* Send PruneEcho(S,G) ? */
655 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
657 if (send_prune_echo
) {
660 rpf
.source_nexthop
.interface
= ifp
;
661 rpf
.rpf_addr
.u
.prefix4
=
662 pim_ifp
->primary_address
;
663 pim_jp_agg_single_upstream_send(
664 &rpf
, ch
->upstream
, 0);
667 ifjoin_to_noinfo(ch
, true);
669 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
670 * message on RP path upon prune timer expiry.
672 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
674 struct pim_upstream
*parent
=
675 ch
->upstream
->parent
;
677 pim_upstream_update_join_desired(pim_ifp
->pim
,
680 pim_jp_agg_single_upstream_send(&parent
->rpf
,
684 /* from here ch may have been deleted */
690 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
691 struct in_addr upstream
, struct prefix_sg
*sg
,
692 uint8_t source_flags
, int holdtime
)
694 struct pim_upstream
*up
;
695 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
697 /* Upstream (S,G) in Joined state ? */
698 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
701 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
704 /* Upstream (S,G) in Joined state */
706 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
707 /* RPF'(S,G) not found */
708 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
709 __PRETTY_FUNCTION__
, up
->sg_str
);
713 /* upstream directed to RPF'(S,G) ? */
714 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
715 char up_str
[INET_ADDRSTRLEN
];
716 char rpf_str
[PREFIX_STRLEN
];
717 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
718 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
721 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
722 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
723 rpf_str
, recv_ifp
->name
);
726 /* upstream directed to RPF'(S,G) */
729 /* Join(S,G) to RPF'(S,G) */
730 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
735 /* Prune to RPF'(S,G) */
737 if (source_flags
& PIM_RPT_BIT_MASK
) {
738 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
739 /* Prune(*,G) to RPF'(S,G) */
740 pim_upstream_join_timer_decrease_to_t_override(
745 /* Prune(S,G,rpt) to RPF'(S,G) */
746 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
751 /* Prune(S,G) to RPF'(S,G) */
752 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
755 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
756 struct in_addr upstream
, struct prefix_sg
*sg
,
757 uint8_t source_flags
, uint16_t holdtime
)
759 struct pim_interface
*recv_pim_ifp
;
760 int is_local
; /* boolean */
762 recv_pim_ifp
= recv_ifp
->info
;
763 zassert(recv_pim_ifp
);
765 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
770 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
771 char up_str
[INET_ADDRSTRLEN
];
772 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
773 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
774 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
775 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
779 * Since recv upstream addr was not directed to our primary
780 * address, check if we should react to it in any way.
782 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
785 return 1; /* non-local */
788 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
789 struct in_addr upstream
, struct prefix_sg
*sg
,
790 uint8_t source_flags
, uint16_t holdtime
)
792 struct pim_interface
*pim_ifp
;
793 struct pim_ifchannel
*ch
;
795 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
800 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
801 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
806 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
808 Transitions from "I am Assert Loser" State
810 Receive Join(S,G) on Interface I
812 We receive a Join(S,G) that has the Upstream Neighbor Address
813 field set to my primary IP address on interface I. The action is
814 to transition to NoInfo state, delete this (S,G) assert state
815 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
818 Notice: The nonlocal_upstream() test above ensures the upstream
819 address of the join message is our primary address.
821 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
822 char neigh_str
[INET_ADDRSTRLEN
];
823 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
825 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
826 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
829 assert_action_a5(ch
);
835 switch (ch
->ifjoin_state
) {
836 case PIM_IFJOIN_NOINFO
:
837 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
839 if (pim_macro_chisin_oiflist(ch
)) {
840 pim_upstream_inherited_olist(pim_ifp
->pim
,
842 pim_forward_start(ch
);
845 * If we are going to be a LHR, we need to note it
847 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
848 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
849 && !(ch
->upstream
->flags
850 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
851 pim_upstream_ref(ch
->upstream
,
852 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
853 __PRETTY_FUNCTION__
);
854 pim_upstream_keep_alive_timer_start(
855 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
858 case PIM_IFJOIN_JOIN
:
859 zassert(!ch
->t_ifjoin_prune_pending_timer
);
862 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
864 previously received join message with holdtime=0xFFFF.
866 if (ch
->t_ifjoin_expiry_timer
) {
867 unsigned long remain
= thread_timer_remain_second(
868 ch
->t_ifjoin_expiry_timer
);
869 if (remain
> holdtime
) {
871 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
874 Transitions from Join State
876 The (S,G) downstream state machine on
877 interface I remains in
878 Join state, and the Expiry Timer (ET) is
880 maximum of its current value and the HoldTime
882 triggering Join/Prune message.
884 Conclusion: Do not change the ET if the
886 higher than the received join holdtime.
891 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
893 case PIM_IFJOIN_PRUNE
:
894 if (source_flags
& PIM_ENCODE_RPT_BIT
)
895 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
898 case PIM_IFJOIN_PRUNE_PENDING
:
899 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
900 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
901 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
902 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
905 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
908 case PIM_IFJOIN_PRUNE_TMP
:
910 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
914 if (holdtime
!= 0xFFFF) {
915 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
916 &ch
->t_ifjoin_expiry_timer
);
920 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
921 struct prefix_sg
*sg
, uint8_t source_flags
,
924 struct pim_ifchannel
*ch
;
925 struct pim_interface
*pim_ifp
;
926 int jp_override_interval_msec
;
928 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
933 ch
= pim_ifchannel_find(ifp
, sg
);
934 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
937 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
938 __PRETTY_FUNCTION__
, ifp
->name
,
939 pim_str_sg_dump(sg
), source_flags
);
943 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
944 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
950 switch (ch
->ifjoin_state
) {
951 case PIM_IFJOIN_NOINFO
:
952 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
953 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
954 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
956 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
957 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
958 jp_override_interval_msec
=
959 pim_if_jp_override_interval_msec(ifp
);
961 jp_override_interval_msec
=
962 0; /* schedule to expire immediately */
963 /* If we called ifjoin_prune() directly instead, care
965 be taken not to use "ch" afterwards since it would be
968 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
969 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
970 thread_add_timer_msec(
971 master
, on_ifjoin_prune_pending_timer
, ch
,
972 jp_override_interval_msec
,
973 &ch
->t_ifjoin_prune_pending_timer
);
974 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
975 holdtime
, &ch
->t_ifjoin_expiry_timer
);
976 pim_upstream_update_join_desired(pim_ifp
->pim
,
980 case PIM_IFJOIN_PRUNE_PENDING
:
983 case PIM_IFJOIN_JOIN
:
984 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
986 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
987 PIM_IFJOIN_PRUNE_PENDING
);
989 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
990 jp_override_interval_msec
=
991 pim_if_jp_override_interval_msec(ifp
);
993 jp_override_interval_msec
=
994 0; /* schedule to expire immediately */
995 /* If we called ifjoin_prune() directly instead, care should
996 be taken not to use "ch" afterwards since it would be
998 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
999 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
1000 jp_override_interval_msec
,
1001 &ch
->t_ifjoin_prune_pending_timer
);
1003 case PIM_IFJOIN_PRUNE
:
1004 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1005 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1006 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1007 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1010 case PIM_IFJOIN_PRUNE_TMP
:
1011 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1012 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1013 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1014 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1015 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1018 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1019 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1020 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1021 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1022 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1023 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1029 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1030 struct prefix_sg
*sg
)
1032 struct pim_ifchannel
*ch
, *starch
;
1033 struct pim_interface
*pim_ifp
;
1034 struct pim_instance
*pim
;
1036 /* PIM enabled on interface? */
1037 pim_ifp
= ifp
->info
;
1040 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1045 /* skip (*,G) ch creation if G is of type SSM */
1046 if (sg
->src
.s_addr
== INADDR_ANY
) {
1047 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1048 if (PIM_DEBUG_PIM_EVENTS
)
1050 "%s: local membership (S,G)=%s ignored as group is SSM",
1051 __PRETTY_FUNCTION__
,
1052 pim_str_sg_dump(sg
));
1057 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1062 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1064 if (sg
->src
.s_addr
== INADDR_ANY
) {
1065 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1066 struct pim_upstream
*child
;
1067 struct listnode
*up_node
;
1071 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1072 if (PIM_DEBUG_EVENTS
)
1073 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1074 __FILE__
, __PRETTY_FUNCTION__
,
1075 child
->sg_str
, ifp
->name
,
1078 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1079 if (pim_upstream_evaluate_join_desired_interface(
1080 child
, ch
, starch
)) {
1081 pim_channel_add_oif(child
->channel_oil
, ifp
,
1082 PIM_OIF_FLAG_PROTO_STAR
);
1083 pim_upstream_switch(pim
, child
,
1084 PIM_UPSTREAM_JOINED
);
1088 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1089 if (pim
->spt
.plist
) {
1090 struct prefix_list
*plist
= prefix_list_lookup(
1091 AFI_IP
, pim
->spt
.plist
);
1094 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1095 g
.u
.prefix4
= up
->sg
.grp
;
1097 if (prefix_list_apply(plist
, &g
)
1099 pim_channel_add_oif(
1100 up
->channel_oil
, pim
->regiface
,
1101 PIM_OIF_FLAG_PROTO_IGMP
);
1105 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1106 PIM_OIF_FLAG_PROTO_IGMP
);
1112 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1113 struct prefix_sg
*sg
)
1115 struct pim_ifchannel
*starch
, *ch
, *orig
;
1116 struct pim_interface
*pim_ifp
;
1118 /* PIM enabled on interface? */
1119 pim_ifp
= ifp
->info
;
1122 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1125 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1128 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1130 if (sg
->src
.s_addr
== INADDR_ANY
) {
1131 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1132 struct pim_upstream
*child
;
1133 struct listnode
*up_node
, *up_nnode
;
1137 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1138 struct channel_oil
*c_oil
= child
->channel_oil
;
1139 struct pim_ifchannel
*chchannel
=
1140 pim_ifchannel_find(ifp
, &child
->sg
);
1141 struct pim_interface
*pim_ifp
= ifp
->info
;
1143 if (PIM_DEBUG_EVENTS
)
1144 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1145 __FILE__
, __PRETTY_FUNCTION__
,
1146 up
->sg_str
, ifp
->name
,
1149 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1151 && !pim_upstream_evaluate_join_desired_interface(
1153 pim_channel_del_oif(c_oil
, ifp
,
1154 PIM_OIF_FLAG_PROTO_STAR
);
1157 * If the S,G has no if channel and the c_oil still
1158 * has output here then the *,G was supplying the
1160 * if channel. So remove it.
1162 if (!chchannel
&& c_oil
1163 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1164 pim_channel_del_oif(c_oil
, ifp
,
1165 PIM_OIF_FLAG_PROTO_STAR
);
1167 /* Child node removal/ref count-- will happen as part of
1168 * parent' delete_no_info */
1171 delete_on_noinfo(orig
);
1174 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1176 int old_couldassert
=
1177 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1178 int new_couldassert
=
1179 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1181 if (new_couldassert
== old_couldassert
)
1184 if (PIM_DEBUG_PIM_EVENTS
) {
1185 char src_str
[INET_ADDRSTRLEN
];
1186 char grp_str
[INET_ADDRSTRLEN
];
1187 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1188 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1189 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1190 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1191 ch
->interface
->name
, old_couldassert
,
1195 if (new_couldassert
) {
1196 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1197 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1199 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1200 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1202 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1203 assert_action_a4(ch
);
1207 pim_ifchannel_update_my_assert_metric(ch
);
1211 my_assert_metric may be affected by:
1214 pim_ifp->primary_address
1215 rpf->source_nexthop.mrib_metric_preference;
1216 rpf->source_nexthop.mrib_route_metric;
1218 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1220 struct pim_assert_metric my_metric_new
=
1221 pim_macro_ch_my_assert_metric_eval(ch
);
1223 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1226 if (PIM_DEBUG_PIM_EVENTS
) {
1227 char src_str
[INET_ADDRSTRLEN
];
1228 char grp_str
[INET_ADDRSTRLEN
];
1229 char old_addr_str
[INET_ADDRSTRLEN
];
1230 char new_addr_str
[INET_ADDRSTRLEN
];
1231 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1232 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1233 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1234 old_addr_str
, sizeof(old_addr_str
));
1235 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1236 new_addr_str
, sizeof(new_addr_str
));
1238 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1239 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1240 ch
->interface
->name
,
1241 ch
->ifassert_my_metric
.rpt_bit_flag
,
1242 ch
->ifassert_my_metric
.metric_preference
,
1243 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1244 my_metric_new
.rpt_bit_flag
,
1245 my_metric_new
.metric_preference
,
1246 my_metric_new
.route_metric
, new_addr_str
);
1249 ch
->ifassert_my_metric
= my_metric_new
;
1251 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1252 &ch
->ifassert_winner_metric
)) {
1253 assert_action_a5(ch
);
1257 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1259 int old_atd
= PIM_FORCE_BOOLEAN(
1260 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1262 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1264 if (new_atd
== old_atd
)
1267 if (PIM_DEBUG_PIM_EVENTS
) {
1268 char src_str
[INET_ADDRSTRLEN
];
1269 char grp_str
[INET_ADDRSTRLEN
];
1270 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1271 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1273 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1274 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1275 ch
->interface
->name
, old_atd
, new_atd
);
1279 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1280 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1282 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1283 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1285 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1286 assert_action_a5(ch
);
1292 * If we have a new pim interface, check to
1293 * see if any of the pre-existing channels have
1294 * their upstream out that way and turn on forwarding
1295 * for that ifchannel then.
1297 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1299 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1300 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1301 struct interface
*ifp
;
1303 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1304 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1305 struct pim_ifchannel
*ch
;
1310 if (new_pim_ifp
== loop_pim_ifp
)
1313 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1314 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1315 struct pim_upstream
*up
= ch
->upstream
;
1316 if ((!up
->channel_oil
)
1317 && (up
->rpf
.source_nexthop
1318 .interface
== new_ifp
))
1319 pim_forward_start(ch
);
1326 * Downstream per-interface (S,G,rpt) state machine
1327 * states that we need to move (S,G,rpt) items
1328 * into different states at the start of the
1329 * reception of a *,G join as well, when
1330 * we get End of Message
1332 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1335 struct pim_ifchannel
*child
;
1336 struct listnode
*ch_node
, *nch_node
;
1337 struct pim_instance
*pim
=
1338 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1340 if (PIM_DEBUG_PIM_TRACE
)
1342 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1343 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1344 ch
->sg_str
, eom
, join
);
1348 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1349 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1352 switch (child
->ifjoin_state
) {
1353 case PIM_IFJOIN_NOINFO
:
1354 case PIM_IFJOIN_JOIN
:
1356 case PIM_IFJOIN_PRUNE
:
1358 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1360 case PIM_IFJOIN_PRUNE_PENDING
:
1362 child
->ifjoin_state
=
1363 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1365 case PIM_IFJOIN_PRUNE_TMP
:
1366 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1370 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1371 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1372 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1373 struct pim_upstream
*parent
= child
->upstream
->parent
;
1375 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1376 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1378 if (I_am_RP(pim
, child
->sg
.grp
)) {
1379 pim_channel_add_oif(
1380 child
->upstream
->channel_oil
,
1381 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1382 pim_upstream_switch(pim
, child
->upstream
,
1383 PIM_UPSTREAM_JOINED
);
1384 pim_jp_agg_single_upstream_send(
1385 &child
->upstream
->rpf
, child
->upstream
,
1389 pim_jp_agg_single_upstream_send(&parent
->rpf
,
1392 delete_on_noinfo(child
);
1398 unsigned int pim_ifchannel_hash_key(void *arg
)
1400 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1402 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);