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
,
48 pim_ifp_rb
, pim_ifchannel_compare
);
50 int pim_ifchannel_compare(const struct pim_ifchannel
*ch1
,
51 const struct pim_ifchannel
*ch2
)
53 struct pim_interface
*pim_ifp1
;
54 struct pim_interface
*pim_ifp2
;
56 pim_ifp1
= ch1
->interface
->info
;
57 pim_ifp2
= ch2
->interface
->info
;
59 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
62 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
65 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
68 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
71 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
74 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
81 * A (*,G) or a (*,*) is going away
82 * remove the parent pointer from
83 * those pointing at us
85 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
87 struct pim_ifchannel
*child
;
92 while (!list_isempty(ch
->sources
)) {
93 child
= listnode_head(ch
->sources
);
95 listnode_delete(ch
->sources
, child
);
100 * A (*,G) or a (*,*) is being created
101 * find all the children that would point
104 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
106 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
107 struct pim_ifchannel
*child
;
109 // Basic Sanity that we are not being silly
110 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
)
111 && (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
114 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
)
115 && (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
118 RB_FOREACH (child
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
) {
119 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
)
120 && (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
)
123 listnode_add_sort(ch
->sources
, child
);
128 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
130 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
133 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
135 struct pim_interface
*pim_ifp
;
137 pim_ifp
= ch
->interface
->info
;
139 if (ch
->upstream
->channel_oil
) {
140 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
141 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
142 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
144 /* SGRpt entry could have empty oil */
145 if (ch
->upstream
->channel_oil
)
146 pim_channel_del_oif(ch
->upstream
->channel_oil
,
147 ch
->interface
, mask
);
149 * Do we have any S,G's that are inheriting?
150 * Nuke from on high too.
152 if (ch
->upstream
->sources
) {
153 struct pim_upstream
*child
;
154 struct listnode
*up_node
;
156 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
158 pim_channel_del_oif(child
->channel_oil
,
160 PIM_OIF_FLAG_PROTO_STAR
);
165 * When this channel is removed
166 * we need to find all our children
167 * and make sure our pointers are fixed
169 pim_ifchannel_remove_children(ch
);
172 list_delete_and_null(&ch
->sources
);
174 listnode_delete(ch
->upstream
->ifchannels
, ch
);
176 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
177 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
180 /* upstream is common across ifchannels, check if upstream's
181 ifchannel list is empty before deleting upstream_del
182 ref count will take care of it.
184 pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __PRETTY_FUNCTION__
);
187 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
188 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
189 THREAD_OFF(ch
->t_ifassert_timer
);
192 listnode_delete(ch
->parent
->sources
, ch
);
196 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
198 if (PIM_DEBUG_PIM_TRACE
)
199 zlog_debug("%s: ifchannel entry %s is deleted ",
200 __PRETTY_FUNCTION__
, ch
->sg_str
);
202 pim_ifchannel_free(ch
);
205 void pim_ifchannel_delete_all(struct interface
*ifp
)
207 struct pim_interface
*pim_ifp
;
208 struct pim_ifchannel
*ch
;
214 while ((ch
= RB_ROOT(pim_ifchannel_rb
,
215 &pim_ifp
->ifchannel_rb
)) != NULL
) {
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
;
643 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
646 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
647 /* Send PruneEcho(S,G) ? */
649 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
651 if (send_prune_echo
) {
654 rpf
.source_nexthop
.interface
= ifp
;
655 rpf
.rpf_addr
.u
.prefix4
=
656 pim_ifp
->primary_address
;
657 pim_jp_agg_single_upstream_send(&rpf
,
662 ifjoin_to_noinfo(ch
, true);
664 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
665 * message on RP path upon prune timer expiry.
667 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
669 pim_upstream_update_join_desired(pim_ifp
->pim
,
672 /* from here ch may have been deleted */
675 "%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
676 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
677 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
683 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
684 struct in_addr upstream
, struct prefix_sg
*sg
,
685 uint8_t source_flags
, int holdtime
)
687 struct pim_upstream
*up
;
688 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
690 /* Upstream (S,G) in Joined state ? */
691 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
694 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
697 /* Upstream (S,G) in Joined state */
699 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
700 /* RPF'(S,G) not found */
701 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
702 __PRETTY_FUNCTION__
, up
->sg_str
);
706 /* upstream directed to RPF'(S,G) ? */
707 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
708 char up_str
[INET_ADDRSTRLEN
];
709 char rpf_str
[PREFIX_STRLEN
];
710 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
711 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
714 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
715 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
716 rpf_str
, recv_ifp
->name
);
719 /* upstream directed to RPF'(S,G) */
722 /* Join(S,G) to RPF'(S,G) */
723 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
728 /* Prune to RPF'(S,G) */
730 if (source_flags
& PIM_RPT_BIT_MASK
) {
731 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
732 /* Prune(*,G) to RPF'(S,G) */
733 pim_upstream_join_timer_decrease_to_t_override(
738 /* Prune(S,G,rpt) to RPF'(S,G) */
739 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
744 /* Prune(S,G) to RPF'(S,G) */
745 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
748 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
749 struct in_addr upstream
, struct prefix_sg
*sg
,
750 uint8_t source_flags
, uint16_t holdtime
)
752 struct pim_interface
*recv_pim_ifp
;
753 int is_local
; /* boolean */
755 recv_pim_ifp
= recv_ifp
->info
;
756 zassert(recv_pim_ifp
);
758 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
763 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
764 char up_str
[INET_ADDRSTRLEN
];
765 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
766 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
767 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
768 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
772 * Since recv upstream addr was not directed to our primary
773 * address, check if we should react to it in any way.
775 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
778 return 1; /* non-local */
781 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
782 struct in_addr upstream
, struct prefix_sg
*sg
,
783 uint8_t source_flags
, uint16_t holdtime
)
785 struct pim_interface
*pim_ifp
;
786 struct pim_ifchannel
*ch
;
788 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
793 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
794 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
799 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
801 Transitions from "I am Assert Loser" State
803 Receive Join(S,G) on Interface I
805 We receive a Join(S,G) that has the Upstream Neighbor Address
806 field set to my primary IP address on interface I. The action is
807 to transition to NoInfo state, delete this (S,G) assert state
808 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
811 Notice: The nonlocal_upstream() test above ensures the upstream
812 address of the join message is our primary address.
814 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
815 char neigh_str
[INET_ADDRSTRLEN
];
816 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
818 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
819 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
822 assert_action_a5(ch
);
828 switch (ch
->ifjoin_state
) {
829 case PIM_IFJOIN_NOINFO
:
830 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
832 if (pim_macro_chisin_oiflist(ch
)) {
833 pim_upstream_inherited_olist(pim_ifp
->pim
,
835 pim_forward_start(ch
);
838 * If we are going to be a LHR, we need to note it
840 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
841 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
842 && !(ch
->upstream
->flags
843 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
844 pim_upstream_ref(ch
->upstream
,
845 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
846 __PRETTY_FUNCTION__
);
847 pim_upstream_keep_alive_timer_start(
848 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
851 case PIM_IFJOIN_JOIN
:
852 zassert(!ch
->t_ifjoin_prune_pending_timer
);
855 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
857 previously received join message with holdtime=0xFFFF.
859 if (ch
->t_ifjoin_expiry_timer
) {
860 unsigned long remain
= thread_timer_remain_second(
861 ch
->t_ifjoin_expiry_timer
);
862 if (remain
> holdtime
) {
864 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
867 Transitions from Join State
869 The (S,G) downstream state machine on
870 interface I remains in
871 Join state, and the Expiry Timer (ET) is
873 maximum of its current value and the HoldTime
875 triggering Join/Prune message.
877 Conclusion: Do not change the ET if the
879 higher than the received join holdtime.
884 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
886 case PIM_IFJOIN_PRUNE
:
887 if (source_flags
& PIM_ENCODE_RPT_BIT
)
888 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
891 case PIM_IFJOIN_PRUNE_PENDING
:
892 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
893 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
894 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
895 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
898 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
901 case PIM_IFJOIN_PRUNE_TMP
:
903 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
907 if (holdtime
!= 0xFFFF) {
908 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
909 &ch
->t_ifjoin_expiry_timer
);
913 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
914 struct prefix_sg
*sg
, uint8_t source_flags
,
917 struct pim_ifchannel
*ch
;
918 struct pim_interface
*pim_ifp
;
919 int jp_override_interval_msec
;
921 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
926 ch
= pim_ifchannel_find(ifp
, sg
);
927 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
930 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
931 __PRETTY_FUNCTION__
, ifp
->name
,
932 pim_str_sg_dump(sg
), source_flags
);
936 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
937 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
943 switch (ch
->ifjoin_state
) {
944 case PIM_IFJOIN_NOINFO
:
945 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
946 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
947 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
949 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
950 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
951 jp_override_interval_msec
=
952 pim_if_jp_override_interval_msec(ifp
);
954 jp_override_interval_msec
=
955 0; /* schedule to expire immediately */
956 /* If we called ifjoin_prune() directly instead, care
958 be taken not to use "ch" afterwards since it would be
961 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
962 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
963 thread_add_timer_msec(
964 master
, on_ifjoin_prune_pending_timer
, ch
,
965 jp_override_interval_msec
,
966 &ch
->t_ifjoin_prune_pending_timer
);
967 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
968 holdtime
, &ch
->t_ifjoin_expiry_timer
);
969 pim_upstream_update_join_desired(pim_ifp
->pim
,
973 case PIM_IFJOIN_PRUNE_PENDING
:
976 case PIM_IFJOIN_JOIN
:
977 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
979 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
980 PIM_IFJOIN_PRUNE_PENDING
);
982 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
983 jp_override_interval_msec
=
984 pim_if_jp_override_interval_msec(ifp
);
986 jp_override_interval_msec
=
987 0; /* schedule to expire immediately */
988 /* If we called ifjoin_prune() directly instead, care should
989 be taken not to use "ch" afterwards since it would be
991 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
992 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
993 jp_override_interval_msec
,
994 &ch
->t_ifjoin_prune_pending_timer
);
996 case PIM_IFJOIN_PRUNE
:
997 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
998 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
999 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1000 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1003 case PIM_IFJOIN_PRUNE_TMP
:
1004 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1005 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1006 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1007 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1008 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1011 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1012 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1013 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1014 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1015 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1016 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1022 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1023 struct prefix_sg
*sg
)
1025 struct pim_ifchannel
*ch
, *starch
;
1026 struct pim_interface
*pim_ifp
;
1027 struct pim_instance
*pim
;
1029 /* PIM enabled on interface? */
1030 pim_ifp
= ifp
->info
;
1033 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1038 /* skip (*,G) ch creation if G is of type SSM */
1039 if (sg
->src
.s_addr
== INADDR_ANY
) {
1040 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1041 if (PIM_DEBUG_PIM_EVENTS
)
1043 "%s: local membership (S,G)=%s ignored as group is SSM",
1044 __PRETTY_FUNCTION__
,
1045 pim_str_sg_dump(sg
));
1050 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1055 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1057 if (sg
->src
.s_addr
== INADDR_ANY
) {
1058 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1059 struct pim_upstream
*child
;
1060 struct listnode
*up_node
;
1064 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1065 if (PIM_DEBUG_EVENTS
)
1066 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1067 __FILE__
, __PRETTY_FUNCTION__
,
1068 child
->sg_str
, ifp
->name
,
1071 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1072 if (pim_upstream_evaluate_join_desired_interface(
1073 child
, ch
, starch
)) {
1074 pim_channel_add_oif(child
->channel_oil
, ifp
,
1075 PIM_OIF_FLAG_PROTO_STAR
);
1076 pim_upstream_switch(pim
, child
,
1077 PIM_UPSTREAM_JOINED
);
1081 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1082 if (pim
->spt
.plist
) {
1083 struct prefix_list
*plist
= prefix_list_lookup(
1084 AFI_IP
, pim
->spt
.plist
);
1087 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1088 g
.u
.prefix4
= up
->sg
.grp
;
1090 if (prefix_list_apply(plist
, &g
)
1092 pim_channel_add_oif(
1093 up
->channel_oil
, pim
->regiface
,
1094 PIM_OIF_FLAG_PROTO_IGMP
);
1098 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1099 PIM_OIF_FLAG_PROTO_IGMP
);
1105 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1106 struct prefix_sg
*sg
)
1108 struct pim_ifchannel
*starch
, *ch
, *orig
;
1109 struct pim_interface
*pim_ifp
;
1111 /* PIM enabled on interface? */
1112 pim_ifp
= ifp
->info
;
1115 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1118 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1121 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1123 if (sg
->src
.s_addr
== INADDR_ANY
) {
1124 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1125 struct pim_upstream
*child
;
1126 struct listnode
*up_node
, *up_nnode
;
1130 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1131 struct channel_oil
*c_oil
= child
->channel_oil
;
1132 struct pim_ifchannel
*chchannel
=
1133 pim_ifchannel_find(ifp
, &child
->sg
);
1134 struct pim_interface
*pim_ifp
= ifp
->info
;
1136 if (PIM_DEBUG_EVENTS
)
1137 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1138 __FILE__
, __PRETTY_FUNCTION__
,
1139 up
->sg_str
, ifp
->name
,
1142 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1144 && !pim_upstream_evaluate_join_desired_interface(
1146 pim_channel_del_oif(c_oil
, ifp
,
1147 PIM_OIF_FLAG_PROTO_STAR
);
1150 * If the S,G has no if channel and the c_oil still
1151 * has output here then the *,G was supplying the
1153 * if channel. So remove it.
1155 if (!chchannel
&& c_oil
1156 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1157 pim_channel_del_oif(c_oil
, ifp
,
1158 PIM_OIF_FLAG_PROTO_STAR
);
1160 /* Child node removal/ref count-- will happen as part of
1161 * parent' delete_no_info */
1164 delete_on_noinfo(orig
);
1167 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1169 int old_couldassert
=
1170 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1171 int new_couldassert
=
1172 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1174 if (new_couldassert
== old_couldassert
)
1177 if (PIM_DEBUG_PIM_EVENTS
) {
1178 char src_str
[INET_ADDRSTRLEN
];
1179 char grp_str
[INET_ADDRSTRLEN
];
1180 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1181 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1182 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1183 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1184 ch
->interface
->name
, old_couldassert
,
1188 if (new_couldassert
) {
1189 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1190 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1192 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1193 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1195 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1196 assert_action_a4(ch
);
1200 pim_ifchannel_update_my_assert_metric(ch
);
1204 my_assert_metric may be affected by:
1207 pim_ifp->primary_address
1208 rpf->source_nexthop.mrib_metric_preference;
1209 rpf->source_nexthop.mrib_route_metric;
1211 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1213 struct pim_assert_metric my_metric_new
=
1214 pim_macro_ch_my_assert_metric_eval(ch
);
1216 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1219 if (PIM_DEBUG_PIM_EVENTS
) {
1220 char src_str
[INET_ADDRSTRLEN
];
1221 char grp_str
[INET_ADDRSTRLEN
];
1222 char old_addr_str
[INET_ADDRSTRLEN
];
1223 char new_addr_str
[INET_ADDRSTRLEN
];
1224 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1225 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1226 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1227 old_addr_str
, sizeof(old_addr_str
));
1228 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1229 new_addr_str
, sizeof(new_addr_str
));
1231 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1232 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1233 ch
->interface
->name
,
1234 ch
->ifassert_my_metric
.rpt_bit_flag
,
1235 ch
->ifassert_my_metric
.metric_preference
,
1236 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1237 my_metric_new
.rpt_bit_flag
,
1238 my_metric_new
.metric_preference
,
1239 my_metric_new
.route_metric
, new_addr_str
);
1242 ch
->ifassert_my_metric
= my_metric_new
;
1244 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1245 &ch
->ifassert_winner_metric
)) {
1246 assert_action_a5(ch
);
1250 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1252 int old_atd
= PIM_FORCE_BOOLEAN(
1253 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1255 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1257 if (new_atd
== old_atd
)
1260 if (PIM_DEBUG_PIM_EVENTS
) {
1261 char src_str
[INET_ADDRSTRLEN
];
1262 char grp_str
[INET_ADDRSTRLEN
];
1263 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1264 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1266 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1267 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1268 ch
->interface
->name
, old_atd
, new_atd
);
1272 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1273 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1275 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1276 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1278 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1279 assert_action_a5(ch
);
1285 * If we have a new pim interface, check to
1286 * see if any of the pre-existing channels have
1287 * their upstream out that way and turn on forwarding
1288 * for that ifchannel then.
1290 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1292 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1293 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1294 struct interface
*ifp
;
1296 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1297 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1298 struct pim_ifchannel
*ch
;
1303 if (new_pim_ifp
== loop_pim_ifp
)
1306 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1307 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1308 struct pim_upstream
*up
= ch
->upstream
;
1309 if ((!up
->channel_oil
)
1310 && (up
->rpf
.source_nexthop
1311 .interface
== new_ifp
))
1312 pim_forward_start(ch
);
1319 * Downstream per-interface (S,G,rpt) state machine
1320 * states that we need to move (S,G,rpt) items
1321 * into different states at the start of the
1322 * reception of a *,G join as well, when
1323 * we get End of Message
1325 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1328 struct pim_ifchannel
*child
;
1329 struct listnode
*ch_node
, *nch_node
;
1330 struct pim_instance
*pim
=
1331 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1333 if (PIM_DEBUG_PIM_TRACE
)
1335 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1336 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1337 ch
->sg_str
, eom
, join
);
1341 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1342 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1345 switch (child
->ifjoin_state
) {
1346 case PIM_IFJOIN_NOINFO
:
1347 case PIM_IFJOIN_JOIN
:
1349 case PIM_IFJOIN_PRUNE
:
1351 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1353 case PIM_IFJOIN_PRUNE_PENDING
:
1355 child
->ifjoin_state
=
1356 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1358 case PIM_IFJOIN_PRUNE_TMP
:
1359 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1363 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1364 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1365 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1366 struct pim_upstream
*parent
=
1367 child
->upstream
->parent
;
1369 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1370 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1372 if (I_am_RP(pim
, child
->sg
.grp
)) {
1373 pim_channel_add_oif(
1374 child
->upstream
->channel_oil
,
1376 PIM_OIF_FLAG_PROTO_STAR
);
1377 pim_upstream_switch(
1378 pim
, child
->upstream
,
1379 PIM_UPSTREAM_JOINED
);
1380 pim_jp_agg_single_upstream_send(
1381 &child
->upstream
->rpf
,
1382 child
->upstream
, true);
1385 pim_jp_agg_single_upstream_send(
1389 delete_on_noinfo(child
);
1395 unsigned int pim_ifchannel_hash_key(void *arg
)
1397 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1399 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);