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
);
557 listnode_delete(ch
->parent
->sources
, ch
);
559 pim_ifchannel_remove_children(ch
);
561 list_delete(ch
->sources
);
563 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
564 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
565 THREAD_OFF(ch
->t_ifassert_timer
);
567 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
568 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
573 listnode_add_sort(up
->ifchannels
, ch
);
575 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
576 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
578 ch
->ifassert_winner
.s_addr
= 0;
581 ch
->t_ifassert_timer
= NULL
;
582 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
583 reset_ifassert_state(ch
);
584 if (pim_macro_ch_could_assert_eval(ch
))
585 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
587 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
589 if (pim_macro_assert_tracking_desired_eval(ch
))
590 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
592 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
594 if (PIM_DEBUG_PIM_TRACE
)
595 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
601 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
603 pim_forward_stop(ch
, !ch_del
);
604 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
606 delete_on_noinfo(ch
);
609 static int on_ifjoin_expiry_timer(struct thread
*t
)
611 struct pim_ifchannel
*ch
;
615 ifjoin_to_noinfo(ch
, true);
616 /* ch may have been deleted */
621 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
623 struct pim_ifchannel
*ch
;
624 int send_prune_echo
; /* boolean */
625 struct interface
*ifp
;
626 struct pim_interface
*pim_ifp
;
630 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
631 /* Send PruneEcho(S,G) ? */
634 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
636 if (send_prune_echo
) {
639 rpf
.source_nexthop
.interface
= ifp
;
640 rpf
.rpf_addr
.u
.prefix4
= pim_ifp
->primary_address
;
641 pim_jp_agg_single_upstream_send(&rpf
, ch
->upstream
, 0);
643 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
644 message on RP path upon prune timer expiry.
646 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
648 pim_upstream_update_join_desired(pim_ifp
->pim
,
651 ch->ifjoin_state transition to NOINFO state
652 ch_del is set to 0 for not deleteing from here.
653 Holdtime expiry (ch_del set to 1) delete the entry.
655 ifjoin_to_noinfo(ch
, false);
657 ifjoin_to_noinfo(ch
, true);
658 /* from here ch may have been deleted */
661 "%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
662 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
663 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
669 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
670 struct in_addr upstream
, struct prefix_sg
*sg
,
671 uint8_t source_flags
, int holdtime
)
673 struct pim_upstream
*up
;
674 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
676 /* Upstream (S,G) in Joined state ? */
677 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
680 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
683 /* Upstream (S,G) in Joined state */
685 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
686 /* RPF'(S,G) not found */
687 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
688 __PRETTY_FUNCTION__
, up
->sg_str
);
692 /* upstream directed to RPF'(S,G) ? */
693 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
694 char up_str
[INET_ADDRSTRLEN
];
695 char rpf_str
[PREFIX_STRLEN
];
696 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
697 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
700 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
701 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
702 rpf_str
, recv_ifp
->name
);
705 /* upstream directed to RPF'(S,G) */
708 /* Join(S,G) to RPF'(S,G) */
709 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
714 /* Prune to RPF'(S,G) */
716 if (source_flags
& PIM_RPT_BIT_MASK
) {
717 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
718 /* Prune(*,G) to RPF'(S,G) */
719 pim_upstream_join_timer_decrease_to_t_override(
724 /* Prune(S,G,rpt) to RPF'(S,G) */
725 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
730 /* Prune(S,G) to RPF'(S,G) */
731 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
734 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
735 struct in_addr upstream
, struct prefix_sg
*sg
,
736 uint8_t source_flags
, uint16_t holdtime
)
738 struct pim_interface
*recv_pim_ifp
;
739 int is_local
; /* boolean */
741 recv_pim_ifp
= recv_ifp
->info
;
742 zassert(recv_pim_ifp
);
744 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
749 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
750 char up_str
[INET_ADDRSTRLEN
];
751 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
752 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
753 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
754 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
758 * Since recv upstream addr was not directed to our primary
759 * address, check if we should react to it in any way.
761 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
764 return 1; /* non-local */
767 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
768 struct in_addr upstream
, struct prefix_sg
*sg
,
769 uint8_t source_flags
, uint16_t holdtime
)
771 struct pim_interface
*pim_ifp
;
772 struct pim_ifchannel
*ch
;
774 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
779 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
780 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
785 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
787 Transitions from "I am Assert Loser" State
789 Receive Join(S,G) on Interface I
791 We receive a Join(S,G) that has the Upstream Neighbor Address
792 field set to my primary IP address on interface I. The action is
793 to transition to NoInfo state, delete this (S,G) assert state
794 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
797 Notice: The nonlocal_upstream() test above ensures the upstream
798 address of the join message is our primary address.
800 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
801 char neigh_str
[INET_ADDRSTRLEN
];
802 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
804 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
805 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
808 assert_action_a5(ch
);
814 switch (ch
->ifjoin_state
) {
815 case PIM_IFJOIN_NOINFO
:
816 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
818 if (pim_macro_chisin_oiflist(ch
)) {
819 pim_upstream_inherited_olist(pim_ifp
->pim
,
821 pim_forward_start(ch
);
824 * If we are going to be a LHR, we need to note it
826 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
827 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
828 && !(ch
->upstream
->flags
829 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
830 pim_upstream_ref(ch
->upstream
,
831 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
832 __PRETTY_FUNCTION__
);
833 pim_upstream_keep_alive_timer_start(
834 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
837 case PIM_IFJOIN_JOIN
:
838 zassert(!ch
->t_ifjoin_prune_pending_timer
);
841 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
843 previously received join message with holdtime=0xFFFF.
845 if (ch
->t_ifjoin_expiry_timer
) {
846 unsigned long remain
= thread_timer_remain_second(
847 ch
->t_ifjoin_expiry_timer
);
848 if (remain
> holdtime
) {
850 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
853 Transitions from Join State
855 The (S,G) downstream state machine on
856 interface I remains in
857 Join state, and the Expiry Timer (ET) is
859 maximum of its current value and the HoldTime
861 triggering Join/Prune message.
863 Conclusion: Do not change the ET if the
865 higher than the received join holdtime.
870 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
872 case PIM_IFJOIN_PRUNE
:
873 if (source_flags
& PIM_ENCODE_RPT_BIT
)
874 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
877 case PIM_IFJOIN_PRUNE_PENDING
:
878 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
879 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
880 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
881 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
884 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
887 case PIM_IFJOIN_PRUNE_TMP
:
889 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
893 if (holdtime
!= 0xFFFF) {
894 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
895 &ch
->t_ifjoin_expiry_timer
);
899 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
900 struct prefix_sg
*sg
, uint8_t source_flags
,
903 struct pim_ifchannel
*ch
;
904 struct pim_interface
*pim_ifp
;
905 int jp_override_interval_msec
;
907 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
912 ch
= pim_ifchannel_find(ifp
, sg
);
913 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
916 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
917 __PRETTY_FUNCTION__
, ifp
->name
,
918 pim_str_sg_dump(sg
), source_flags
);
922 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
923 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
929 switch (ch
->ifjoin_state
) {
930 case PIM_IFJOIN_NOINFO
:
931 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
932 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
933 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
935 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
936 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
937 jp_override_interval_msec
=
938 pim_if_jp_override_interval_msec(ifp
);
940 jp_override_interval_msec
=
941 0; /* schedule to expire immediately */
942 /* If we called ifjoin_prune() directly instead, care
944 be taken not to use "ch" afterwards since it would be
947 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
948 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
949 thread_add_timer_msec(
950 master
, on_ifjoin_prune_pending_timer
, ch
,
951 jp_override_interval_msec
,
952 &ch
->t_ifjoin_prune_pending_timer
);
953 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
954 holdtime
, &ch
->t_ifjoin_expiry_timer
);
955 pim_upstream_update_join_desired(pim_ifp
->pim
,
959 case PIM_IFJOIN_PRUNE_PENDING
:
962 case PIM_IFJOIN_JOIN
:
963 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
965 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
966 PIM_IFJOIN_PRUNE_PENDING
);
968 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
969 jp_override_interval_msec
=
970 pim_if_jp_override_interval_msec(ifp
);
972 jp_override_interval_msec
=
973 0; /* schedule to expire immediately */
974 /* If we called ifjoin_prune() directly instead, care should
975 be taken not to use "ch" afterwards since it would be
977 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
978 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
979 jp_override_interval_msec
,
980 &ch
->t_ifjoin_prune_pending_timer
);
982 case PIM_IFJOIN_PRUNE
:
983 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
984 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
985 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
986 holdtime
, &ch
->t_ifjoin_expiry_timer
);
989 case PIM_IFJOIN_PRUNE_TMP
:
990 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
991 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
992 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
993 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
994 holdtime
, &ch
->t_ifjoin_expiry_timer
);
997 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
998 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
999 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1000 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1001 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1002 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1008 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1009 struct prefix_sg
*sg
)
1011 struct pim_ifchannel
*ch
, *starch
;
1012 struct pim_interface
*pim_ifp
;
1013 struct pim_instance
*pim
;
1015 /* PIM enabled on interface? */
1016 pim_ifp
= ifp
->info
;
1019 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1024 /* skip (*,G) ch creation if G is of type SSM */
1025 if (sg
->src
.s_addr
== INADDR_ANY
) {
1026 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1027 if (PIM_DEBUG_PIM_EVENTS
)
1029 "%s: local membership (S,G)=%s ignored as group is SSM",
1030 __PRETTY_FUNCTION__
,
1031 pim_str_sg_dump(sg
));
1036 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1041 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1043 if (sg
->src
.s_addr
== INADDR_ANY
) {
1044 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1045 struct pim_upstream
*child
;
1046 struct listnode
*up_node
;
1050 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1051 if (PIM_DEBUG_EVENTS
)
1052 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1053 __FILE__
, __PRETTY_FUNCTION__
,
1054 child
->sg_str
, ifp
->name
,
1057 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1058 if (pim_upstream_evaluate_join_desired_interface(
1059 child
, ch
, starch
)) {
1060 pim_channel_add_oif(child
->channel_oil
, ifp
,
1061 PIM_OIF_FLAG_PROTO_STAR
);
1062 pim_upstream_switch(pim
, child
,
1063 PIM_UPSTREAM_JOINED
);
1067 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1068 if (pim
->spt
.plist
) {
1069 struct prefix_list
*plist
= prefix_list_lookup(
1070 AFI_IP
, pim
->spt
.plist
);
1073 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1074 g
.u
.prefix4
= up
->sg
.grp
;
1076 if (prefix_list_apply(plist
, &g
)
1078 pim_channel_add_oif(
1079 up
->channel_oil
, pim
->regiface
,
1080 PIM_OIF_FLAG_PROTO_IGMP
);
1084 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1085 PIM_OIF_FLAG_PROTO_IGMP
);
1091 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1092 struct prefix_sg
*sg
)
1094 struct pim_ifchannel
*starch
, *ch
, *orig
;
1095 struct pim_interface
*pim_ifp
;
1097 /* PIM enabled on interface? */
1098 pim_ifp
= ifp
->info
;
1101 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1104 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1108 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1110 if (sg
->src
.s_addr
== INADDR_ANY
) {
1111 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1112 struct pim_upstream
*child
;
1113 struct listnode
*up_node
, *up_nnode
;
1117 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1118 struct channel_oil
*c_oil
= child
->channel_oil
;
1119 struct pim_ifchannel
*chchannel
=
1120 pim_ifchannel_find(ifp
, &child
->sg
);
1121 struct pim_interface
*pim_ifp
= ifp
->info
;
1123 if (PIM_DEBUG_EVENTS
)
1124 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1125 __FILE__
, __PRETTY_FUNCTION__
,
1126 up
->sg_str
, ifp
->name
,
1129 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1131 && !pim_upstream_evaluate_join_desired_interface(
1133 pim_channel_del_oif(c_oil
, ifp
,
1134 PIM_OIF_FLAG_PROTO_STAR
);
1137 * If the S,G has no if channel and the c_oil still
1138 * has output here then the *,G was supplying the
1140 * if channel. So remove it.
1142 if (!chchannel
&& c_oil
1143 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1144 pim_channel_del_oif(c_oil
, ifp
,
1145 PIM_OIF_FLAG_PROTO_STAR
);
1147 /* Child node removal/ref count-- will happen as part of
1148 * parent' delete_no_info */
1151 delete_on_noinfo(orig
);
1154 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1156 int old_couldassert
=
1157 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1158 int new_couldassert
=
1159 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1161 if (new_couldassert
== old_couldassert
)
1164 if (PIM_DEBUG_PIM_EVENTS
) {
1165 char src_str
[INET_ADDRSTRLEN
];
1166 char grp_str
[INET_ADDRSTRLEN
];
1167 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1168 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1169 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1170 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1171 ch
->interface
->name
, old_couldassert
,
1175 if (new_couldassert
) {
1176 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1177 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1179 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1180 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1182 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1183 assert_action_a4(ch
);
1187 pim_ifchannel_update_my_assert_metric(ch
);
1191 my_assert_metric may be affected by:
1194 pim_ifp->primary_address
1195 rpf->source_nexthop.mrib_metric_preference;
1196 rpf->source_nexthop.mrib_route_metric;
1198 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1200 struct pim_assert_metric my_metric_new
=
1201 pim_macro_ch_my_assert_metric_eval(ch
);
1203 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1206 if (PIM_DEBUG_PIM_EVENTS
) {
1207 char src_str
[INET_ADDRSTRLEN
];
1208 char grp_str
[INET_ADDRSTRLEN
];
1209 char old_addr_str
[INET_ADDRSTRLEN
];
1210 char new_addr_str
[INET_ADDRSTRLEN
];
1211 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1212 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1213 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1214 old_addr_str
, sizeof(old_addr_str
));
1215 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1216 new_addr_str
, sizeof(new_addr_str
));
1218 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1219 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1220 ch
->interface
->name
,
1221 ch
->ifassert_my_metric
.rpt_bit_flag
,
1222 ch
->ifassert_my_metric
.metric_preference
,
1223 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1224 my_metric_new
.rpt_bit_flag
,
1225 my_metric_new
.metric_preference
,
1226 my_metric_new
.route_metric
, new_addr_str
);
1229 ch
->ifassert_my_metric
= my_metric_new
;
1231 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1232 &ch
->ifassert_winner_metric
)) {
1233 assert_action_a5(ch
);
1237 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1239 int old_atd
= PIM_FORCE_BOOLEAN(
1240 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1242 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1244 if (new_atd
== old_atd
)
1247 if (PIM_DEBUG_PIM_EVENTS
) {
1248 char src_str
[INET_ADDRSTRLEN
];
1249 char grp_str
[INET_ADDRSTRLEN
];
1250 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1251 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1253 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1254 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1255 ch
->interface
->name
, old_atd
, new_atd
);
1259 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1260 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1262 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1263 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1265 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1266 assert_action_a5(ch
);
1272 * If we have a new pim interface, check to
1273 * see if any of the pre-existing channels have
1274 * their upstream out that way and turn on forwarding
1275 * for that ifchannel then.
1277 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1279 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1280 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1281 struct listnode
*ifnode
;
1282 struct interface
*ifp
;
1284 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(pim
->vrf_id
), ifnode
, ifp
)) {
1285 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1286 struct pim_ifchannel
*ch
;
1291 if (new_pim_ifp
== loop_pim_ifp
)
1294 RB_FOREACH(ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1295 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1296 struct pim_upstream
*up
= ch
->upstream
;
1297 if ((!up
->channel_oil
)
1298 && (up
->rpf
.source_nexthop
1299 .interface
== new_ifp
))
1300 pim_forward_start(ch
);
1307 * Downstream per-interface (S,G,rpt) state machine
1308 * states that we need to move (S,G,rpt) items
1309 * into different states at the start of the
1310 * reception of a *,G join as well, when
1311 * we get End of Message
1313 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1314 uint8_t source_flags
, uint8_t join
,
1315 uint8_t starg_alone
)
1317 struct pim_ifchannel
*child
;
1318 struct listnode
*ch_node
;
1320 if (PIM_DEBUG_PIM_TRACE
)
1322 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1323 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1324 ch
->sg_str
, eom
, join
);
1328 for (ALL_LIST_ELEMENTS_RO(ch
->sources
, ch_node
, child
)) {
1329 /* Only *,G Join received and no (SG-RPT) prune.
1330 eom = 1, only (W,G) join_alone is true, WC and RPT are set.
1331 Scan all S,G associated to G and if any SG-RPT
1332 remove the SG-RPT flag.
1334 if (eom
&& starg_alone
&& (source_flags
& PIM_RPT_BIT_MASK
)
1335 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
1336 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
1337 struct pim_upstream
*up
= child
->upstream
;
1339 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1341 if (PIM_DEBUG_TRACE
)
1343 "%s: SGRpt flag is cleared, add inherit oif to up %s",
1344 __PRETTY_FUNCTION__
,
1346 pim_channel_add_oif(
1347 up
->channel_oil
, ch
->interface
,
1348 PIM_OIF_FLAG_PROTO_STAR
);
1349 pim_ifchannel_ifjoin_switch(
1350 __PRETTY_FUNCTION__
, child
,
1356 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1359 switch (child
->ifjoin_state
) {
1360 case PIM_IFJOIN_NOINFO
:
1361 case PIM_IFJOIN_JOIN
:
1363 case PIM_IFJOIN_PRUNE
:
1365 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1367 case PIM_IFJOIN_PRUNE_PENDING
:
1369 child
->ifjoin_state
=
1370 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1372 case PIM_IFJOIN_PRUNE_TMP
:
1373 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1375 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1381 unsigned int pim_ifchannel_hash_key(void *arg
)
1383 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1385 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);