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_delete(struct pim_ifchannel
*ch
)
129 struct pim_interface
*pim_ifp
;
131 pim_ifp
= ch
->interface
->info
;
133 if (ch
->upstream
->channel_oil
) {
134 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
135 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
136 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
138 /* SGRpt entry could have empty oil */
139 pim_channel_del_oif(ch
->upstream
->channel_oil
, ch
->interface
,
142 * Do we have any S,G's that are inheriting?
143 * Nuke from on high too.
145 if (ch
->upstream
->sources
) {
146 struct pim_upstream
*child
;
147 struct listnode
*up_node
;
149 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
151 pim_channel_del_oif(child
->channel_oil
,
153 PIM_OIF_FLAG_PROTO_STAR
);
158 * When this channel is removed
159 * we need to find all our children
160 * and make sure our pointers are fixed
162 pim_ifchannel_remove_children(ch
);
165 list_delete_and_null(&ch
->sources
);
167 listnode_delete(ch
->upstream
->ifchannels
, ch
);
169 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
170 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
173 /* upstream is common across ifchannels, check if upstream's
174 ifchannel list is empty before deleting upstream_del
175 ref count will take care of it.
177 pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __PRETTY_FUNCTION__
);
180 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
181 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
182 THREAD_OFF(ch
->t_ifassert_timer
);
185 listnode_delete(ch
->parent
->sources
, ch
);
189 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
191 if (PIM_DEBUG_PIM_TRACE
)
192 zlog_debug("%s: ifchannel entry %s is deleted ",
193 __PRETTY_FUNCTION__
, ch
->sg_str
);
195 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
198 void pim_ifchannel_delete_all(struct interface
*ifp
)
200 struct pim_interface
*pim_ifp
;
201 struct pim_ifchannel
*ch
;
207 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
208 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
210 pim_ifchannel_delete(ch
);
214 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
216 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
217 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
218 && ch
->t_ifjoin_expiry_timer
== NULL
)
219 pim_ifchannel_delete(ch
);
222 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
223 enum pim_ifjoin_state new_state
)
225 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
226 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
228 if (PIM_DEBUG_PIM_EVENTS
)
230 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
231 ch
->interface
->name
, ch
->sg_str
,
232 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
233 pim_ifchannel_ifjoin_name(new_state
, 0));
236 if (old_state
== new_state
) {
237 if (PIM_DEBUG_PIM_EVENTS
) {
239 "%s calledby %s: non-transition on state %d (%s)",
240 __PRETTY_FUNCTION__
, caller
, new_state
,
241 pim_ifchannel_ifjoin_name(new_state
, 0));
246 ch
->ifjoin_state
= new_state
;
248 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
249 struct pim_upstream
*up
= ch
->upstream
;
250 struct pim_upstream
*child
;
251 struct listnode
*up_node
;
254 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
255 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
257 struct channel_oil
*c_oil
=
260 if (PIM_DEBUG_PIM_TRACE
)
262 "%s %s: Prune(S,G)=%s from %s",
270 if (!pim_upstream_evaluate_join_desired(
271 pim_ifp
->pim
, child
)) {
273 c_oil
, ch
->interface
,
274 PIM_OIF_FLAG_PROTO_STAR
);
275 pim_upstream_update_join_desired(
276 pim_ifp
->pim
, child
);
280 * If the S,G has no if channel and the
282 * has output here then the *,G was
283 * supplying the implied
284 * if channel. So remove it.
285 * I think this is dead code now. is it?
287 if (c_oil
->oil
.mfcc_ttls
288 [pim_ifp
->mroute_vif_index
])
290 c_oil
, ch
->interface
,
291 PIM_OIF_FLAG_PROTO_STAR
);
294 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
295 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
297 if (PIM_DEBUG_PIM_TRACE
)
299 "%s %s: Join(S,G)=%s from %s",
305 if (pim_upstream_evaluate_join_desired(
306 pim_ifp
->pim
, child
)) {
310 PIM_OIF_FLAG_PROTO_STAR
);
311 pim_upstream_update_join_desired(
312 pim_ifp
->pim
, child
);
318 /* Transition to/from NOINFO ? */
319 if ((old_state
== PIM_IFJOIN_NOINFO
)
320 || (new_state
== PIM_IFJOIN_NOINFO
)) {
322 if (PIM_DEBUG_PIM_EVENTS
) {
323 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
324 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
326 ch
->sg_str
, ch
->interface
->name
);
330 Record uptime of state transition to/from NOINFO
332 ch
->ifjoin_creation
= pim_time_monotonic_sec();
334 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
335 pim_ifchannel_update_could_assert(ch
);
336 pim_ifchannel_update_assert_tracking_desired(ch
);
340 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
343 switch (ifjoin_state
) {
344 case PIM_IFJOIN_NOINFO
:
345 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
350 case PIM_IFJOIN_JOIN
:
353 case PIM_IFJOIN_PRUNE
:
354 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
359 case PIM_IFJOIN_PRUNE_PENDING
:
360 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
365 case PIM_IFJOIN_PRUNE_TMP
:
366 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
371 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
372 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
379 return "ifjoin_bad_state";
382 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
384 switch (ifassert_state
) {
385 case PIM_IFASSERT_NOINFO
:
387 case PIM_IFASSERT_I_AM_WINNER
:
389 case PIM_IFASSERT_I_AM_LOSER
:
393 return "ifassert_bad_state";
397 RFC 4601: 4.6.5. Assert State Macros
399 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
400 defaults to Infinity when in the NoInfo state.
402 void reset_ifassert_state(struct pim_ifchannel
*ch
)
404 struct in_addr any
= {.s_addr
= INADDR_ANY
};
406 THREAD_OFF(ch
->t_ifassert_timer
);
408 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
409 qpim_infinite_assert_metric
);
412 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
413 struct prefix_sg
*sg
)
415 struct pim_interface
*pim_ifp
;
416 struct pim_ifchannel
*ch
;
417 struct pim_ifchannel lookup
;
422 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
423 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
428 lookup
.interface
= ifp
;
429 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
434 static void ifmembership_set(struct pim_ifchannel
*ch
,
435 enum pim_ifmembership membership
)
437 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
439 if (ch
->local_ifmembership
== membership
)
442 if (PIM_DEBUG_PIM_EVENTS
) {
443 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
444 __PRETTY_FUNCTION__
, ch
->sg_str
,
445 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
447 ch
->interface
->name
);
450 ch
->local_ifmembership
= membership
;
452 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
453 pim_ifchannel_update_could_assert(ch
);
454 pim_ifchannel_update_assert_tracking_desired(ch
);
458 void pim_ifchannel_membership_clear(struct interface
*ifp
)
460 struct pim_interface
*pim_ifp
;
461 struct pim_ifchannel
*ch
;
466 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
467 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
470 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
472 struct pim_interface
*pim_ifp
;
473 struct pim_ifchannel
*ch
, *ch_tmp
;
478 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
479 delete_on_noinfo(ch
);
483 * For a given Interface, if we are given a S,G
484 * Find the *,G (If we have it).
485 * If we are passed a *,G, find the *,* ifchannel
488 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
490 struct prefix_sg parent_sg
= ch
->sg
;
491 struct pim_ifchannel
*parent
= NULL
;
494 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
495 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
496 parent_sg
.src
.s_addr
= INADDR_ANY
;
497 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
500 listnode_add(parent
->sources
, ch
);
507 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
508 struct prefix_sg
*sg
,
509 uint8_t source_flags
, int up_flags
)
511 struct pim_interface
*pim_ifp
;
512 struct pim_ifchannel
*ch
;
513 struct pim_upstream
*up
;
515 ch
= pim_ifchannel_find(ifp
, sg
);
521 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
524 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
525 && !(source_flags
& PIM_ENCODE_WC_BIT
))
526 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
530 pim_str_sg_set(sg
, ch
->sg_str
);
531 ch
->parent
= pim_ifchannel_find_parent(ch
);
532 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
533 ch
->sources
= list_new();
535 (int (*)(void *, void *))pim_ifchannel_compare
;
539 pim_ifchannel_find_new_children(ch
);
540 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
542 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
543 ch
->t_ifjoin_expiry_timer
= NULL
;
544 ch
->t_ifjoin_prune_pending_timer
= NULL
;
545 ch
->ifjoin_creation
= 0;
547 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
549 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
,
550 __PRETTY_FUNCTION__
, ch
);
554 "%s: could not attach upstream (S,G)=%s on interface %s",
555 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
558 listnode_delete(ch
->parent
->sources
, ch
);
560 pim_ifchannel_remove_children(ch
);
562 list_delete_and_null(&ch
->sources
);
564 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
565 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
566 THREAD_OFF(ch
->t_ifassert_timer
);
568 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
569 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
574 listnode_add_sort(up
->ifchannels
, ch
);
576 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
577 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
579 ch
->ifassert_winner
.s_addr
= 0;
582 ch
->t_ifassert_timer
= NULL
;
583 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
584 reset_ifassert_state(ch
);
585 if (pim_macro_ch_could_assert_eval(ch
))
586 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
588 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
590 if (pim_macro_assert_tracking_desired_eval(ch
))
591 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
593 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
595 if (PIM_DEBUG_PIM_TRACE
)
596 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
602 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
604 pim_forward_stop(ch
, !ch_del
);
605 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
607 delete_on_noinfo(ch
);
610 static int on_ifjoin_expiry_timer(struct thread
*t
)
612 struct pim_ifchannel
*ch
;
616 ifjoin_to_noinfo(ch
, true);
617 /* ch may have been deleted */
622 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
624 struct pim_ifchannel
*ch
;
625 int send_prune_echo
; /* boolean */
626 struct interface
*ifp
;
627 struct pim_interface
*pim_ifp
;
633 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
634 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
635 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
637 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
640 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
641 /* Send PruneEcho(S,G) ? */
643 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
645 if (send_prune_echo
) {
648 rpf
.source_nexthop
.interface
= ifp
;
649 rpf
.rpf_addr
.u
.prefix4
=
650 pim_ifp
->primary_address
;
651 pim_jp_agg_single_upstream_send(
652 &rpf
, ch
->upstream
, 0);
655 ifjoin_to_noinfo(ch
, true);
657 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
658 * message on RP path upon prune timer expiry.
660 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
662 struct pim_upstream
*parent
=
663 ch
->upstream
->parent
;
665 pim_upstream_update_join_desired(pim_ifp
->pim
,
668 pim_jp_agg_single_upstream_send(&parent
->rpf
,
672 /* from here ch may have been deleted */
678 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
679 struct in_addr upstream
, struct prefix_sg
*sg
,
680 uint8_t source_flags
, int holdtime
)
682 struct pim_upstream
*up
;
683 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
685 /* Upstream (S,G) in Joined state ? */
686 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
689 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
692 /* Upstream (S,G) in Joined state */
694 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
695 /* RPF'(S,G) not found */
696 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
697 __PRETTY_FUNCTION__
, up
->sg_str
);
701 /* upstream directed to RPF'(S,G) ? */
702 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
703 char up_str
[INET_ADDRSTRLEN
];
704 char rpf_str
[PREFIX_STRLEN
];
705 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
706 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
709 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
710 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
711 rpf_str
, recv_ifp
->name
);
714 /* upstream directed to RPF'(S,G) */
717 /* Join(S,G) to RPF'(S,G) */
718 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
723 /* Prune to RPF'(S,G) */
725 if (source_flags
& PIM_RPT_BIT_MASK
) {
726 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
727 /* Prune(*,G) to RPF'(S,G) */
728 pim_upstream_join_timer_decrease_to_t_override(
733 /* Prune(S,G,rpt) to RPF'(S,G) */
734 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
739 /* Prune(S,G) to RPF'(S,G) */
740 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
743 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
744 struct in_addr upstream
, struct prefix_sg
*sg
,
745 uint8_t source_flags
, uint16_t holdtime
)
747 struct pim_interface
*recv_pim_ifp
;
748 int is_local
; /* boolean */
750 recv_pim_ifp
= recv_ifp
->info
;
751 zassert(recv_pim_ifp
);
753 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
758 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
759 char up_str
[INET_ADDRSTRLEN
];
760 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
761 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
762 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
763 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
767 * Since recv upstream addr was not directed to our primary
768 * address, check if we should react to it in any way.
770 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
773 return 1; /* non-local */
776 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
777 struct in_addr upstream
, struct prefix_sg
*sg
,
778 uint8_t source_flags
, uint16_t holdtime
)
780 struct pim_interface
*pim_ifp
;
781 struct pim_ifchannel
*ch
;
783 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
788 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
789 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
794 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
796 Transitions from "I am Assert Loser" State
798 Receive Join(S,G) on Interface I
800 We receive a Join(S,G) that has the Upstream Neighbor Address
801 field set to my primary IP address on interface I. The action is
802 to transition to NoInfo state, delete this (S,G) assert state
803 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
806 Notice: The nonlocal_upstream() test above ensures the upstream
807 address of the join message is our primary address.
809 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
810 char neigh_str
[INET_ADDRSTRLEN
];
811 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
813 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
814 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
817 assert_action_a5(ch
);
823 switch (ch
->ifjoin_state
) {
824 case PIM_IFJOIN_NOINFO
:
825 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
827 if (pim_macro_chisin_oiflist(ch
)) {
828 pim_upstream_inherited_olist(pim_ifp
->pim
,
830 pim_forward_start(ch
);
833 * If we are going to be a LHR, we need to note it
835 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
836 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
837 && !(ch
->upstream
->flags
838 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
839 pim_upstream_ref(ch
->upstream
,
840 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
841 __PRETTY_FUNCTION__
);
842 pim_upstream_keep_alive_timer_start(
843 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
846 case PIM_IFJOIN_JOIN
:
847 zassert(!ch
->t_ifjoin_prune_pending_timer
);
850 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
852 previously received join message with holdtime=0xFFFF.
854 if (ch
->t_ifjoin_expiry_timer
) {
855 unsigned long remain
= thread_timer_remain_second(
856 ch
->t_ifjoin_expiry_timer
);
857 if (remain
> holdtime
) {
859 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
862 Transitions from Join State
864 The (S,G) downstream state machine on
865 interface I remains in
866 Join state, and the Expiry Timer (ET) is
868 maximum of its current value and the HoldTime
870 triggering Join/Prune message.
872 Conclusion: Do not change the ET if the
874 higher than the received join holdtime.
879 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
881 case PIM_IFJOIN_PRUNE
:
882 if (source_flags
& PIM_ENCODE_RPT_BIT
)
883 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
886 case PIM_IFJOIN_PRUNE_PENDING
:
887 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
888 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
889 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
890 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
893 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
896 case PIM_IFJOIN_PRUNE_TMP
:
898 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
902 if (holdtime
!= 0xFFFF) {
903 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
904 &ch
->t_ifjoin_expiry_timer
);
908 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
909 struct prefix_sg
*sg
, uint8_t source_flags
,
912 struct pim_ifchannel
*ch
;
913 struct pim_interface
*pim_ifp
;
914 int jp_override_interval_msec
;
916 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
921 ch
= pim_ifchannel_find(ifp
, sg
);
922 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
925 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
926 __PRETTY_FUNCTION__
, ifp
->name
,
927 pim_str_sg_dump(sg
), source_flags
);
931 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
932 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
938 switch (ch
->ifjoin_state
) {
939 case PIM_IFJOIN_NOINFO
:
940 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
941 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
942 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
944 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
945 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
946 jp_override_interval_msec
=
947 pim_if_jp_override_interval_msec(ifp
);
949 jp_override_interval_msec
=
950 0; /* schedule to expire immediately */
951 /* If we called ifjoin_prune() directly instead, care
953 be taken not to use "ch" afterwards since it would be
956 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
957 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
958 thread_add_timer_msec(
959 master
, on_ifjoin_prune_pending_timer
, ch
,
960 jp_override_interval_msec
,
961 &ch
->t_ifjoin_prune_pending_timer
);
962 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
963 holdtime
, &ch
->t_ifjoin_expiry_timer
);
964 pim_upstream_update_join_desired(pim_ifp
->pim
,
968 case PIM_IFJOIN_PRUNE_PENDING
:
971 case PIM_IFJOIN_JOIN
:
972 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
974 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
975 PIM_IFJOIN_PRUNE_PENDING
);
977 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
978 jp_override_interval_msec
=
979 pim_if_jp_override_interval_msec(ifp
);
981 jp_override_interval_msec
=
982 0; /* schedule to expire immediately */
983 /* If we called ifjoin_prune() directly instead, care should
984 be taken not to use "ch" afterwards since it would be
986 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
987 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
988 jp_override_interval_msec
,
989 &ch
->t_ifjoin_prune_pending_timer
);
991 case PIM_IFJOIN_PRUNE
:
992 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
993 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
994 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
995 holdtime
, &ch
->t_ifjoin_expiry_timer
);
998 case PIM_IFJOIN_PRUNE_TMP
:
999 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1000 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1001 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1002 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1003 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1006 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1007 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1008 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1009 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1010 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1011 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1017 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1018 struct prefix_sg
*sg
)
1020 struct pim_ifchannel
*ch
, *starch
;
1021 struct pim_interface
*pim_ifp
;
1022 struct pim_instance
*pim
;
1024 /* PIM enabled on interface? */
1025 pim_ifp
= ifp
->info
;
1028 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1033 /* skip (*,G) ch creation if G is of type SSM */
1034 if (sg
->src
.s_addr
== INADDR_ANY
) {
1035 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1036 if (PIM_DEBUG_PIM_EVENTS
)
1038 "%s: local membership (S,G)=%s ignored as group is SSM",
1039 __PRETTY_FUNCTION__
,
1040 pim_str_sg_dump(sg
));
1045 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1050 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1052 if (sg
->src
.s_addr
== INADDR_ANY
) {
1053 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1054 struct pim_upstream
*child
;
1055 struct listnode
*up_node
;
1059 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1060 if (PIM_DEBUG_EVENTS
)
1061 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1062 __FILE__
, __PRETTY_FUNCTION__
,
1063 child
->sg_str
, ifp
->name
,
1066 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1067 if (pim_upstream_evaluate_join_desired_interface(
1068 child
, ch
, starch
)) {
1069 pim_channel_add_oif(child
->channel_oil
, ifp
,
1070 PIM_OIF_FLAG_PROTO_STAR
);
1071 pim_upstream_switch(pim
, child
,
1072 PIM_UPSTREAM_JOINED
);
1076 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1077 if (pim
->spt
.plist
) {
1078 struct prefix_list
*plist
= prefix_list_lookup(
1079 AFI_IP
, pim
->spt
.plist
);
1082 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1083 g
.u
.prefix4
= up
->sg
.grp
;
1085 if (prefix_list_apply(plist
, &g
)
1087 pim_channel_add_oif(
1088 up
->channel_oil
, pim
->regiface
,
1089 PIM_OIF_FLAG_PROTO_IGMP
);
1093 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1094 PIM_OIF_FLAG_PROTO_IGMP
);
1100 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1101 struct prefix_sg
*sg
)
1103 struct pim_ifchannel
*starch
, *ch
, *orig
;
1104 struct pim_interface
*pim_ifp
;
1106 /* PIM enabled on interface? */
1107 pim_ifp
= ifp
->info
;
1110 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1113 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1116 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1118 if (sg
->src
.s_addr
== INADDR_ANY
) {
1119 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1120 struct pim_upstream
*child
;
1121 struct listnode
*up_node
, *up_nnode
;
1125 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1126 struct channel_oil
*c_oil
= child
->channel_oil
;
1127 struct pim_ifchannel
*chchannel
=
1128 pim_ifchannel_find(ifp
, &child
->sg
);
1129 struct pim_interface
*pim_ifp
= ifp
->info
;
1131 if (PIM_DEBUG_EVENTS
)
1132 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1133 __FILE__
, __PRETTY_FUNCTION__
,
1134 up
->sg_str
, ifp
->name
,
1137 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1139 && !pim_upstream_evaluate_join_desired_interface(
1141 pim_channel_del_oif(c_oil
, ifp
,
1142 PIM_OIF_FLAG_PROTO_STAR
);
1145 * If the S,G has no if channel and the c_oil still
1146 * has output here then the *,G was supplying the
1148 * if channel. So remove it.
1150 if (!chchannel
&& c_oil
1151 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1152 pim_channel_del_oif(c_oil
, ifp
,
1153 PIM_OIF_FLAG_PROTO_STAR
);
1155 /* Child node removal/ref count-- will happen as part of
1156 * parent' delete_no_info */
1159 delete_on_noinfo(orig
);
1162 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1164 int old_couldassert
=
1165 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1166 int new_couldassert
=
1167 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1169 if (new_couldassert
== old_couldassert
)
1172 if (PIM_DEBUG_PIM_EVENTS
) {
1173 char src_str
[INET_ADDRSTRLEN
];
1174 char grp_str
[INET_ADDRSTRLEN
];
1175 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1176 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1177 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1178 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1179 ch
->interface
->name
, old_couldassert
,
1183 if (new_couldassert
) {
1184 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1185 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1187 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1188 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1190 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1191 assert_action_a4(ch
);
1195 pim_ifchannel_update_my_assert_metric(ch
);
1199 my_assert_metric may be affected by:
1202 pim_ifp->primary_address
1203 rpf->source_nexthop.mrib_metric_preference;
1204 rpf->source_nexthop.mrib_route_metric;
1206 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1208 struct pim_assert_metric my_metric_new
=
1209 pim_macro_ch_my_assert_metric_eval(ch
);
1211 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1214 if (PIM_DEBUG_PIM_EVENTS
) {
1215 char src_str
[INET_ADDRSTRLEN
];
1216 char grp_str
[INET_ADDRSTRLEN
];
1217 char old_addr_str
[INET_ADDRSTRLEN
];
1218 char new_addr_str
[INET_ADDRSTRLEN
];
1219 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1220 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1221 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1222 old_addr_str
, sizeof(old_addr_str
));
1223 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1224 new_addr_str
, sizeof(new_addr_str
));
1226 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1227 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1228 ch
->interface
->name
,
1229 ch
->ifassert_my_metric
.rpt_bit_flag
,
1230 ch
->ifassert_my_metric
.metric_preference
,
1231 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1232 my_metric_new
.rpt_bit_flag
,
1233 my_metric_new
.metric_preference
,
1234 my_metric_new
.route_metric
, new_addr_str
);
1237 ch
->ifassert_my_metric
= my_metric_new
;
1239 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1240 &ch
->ifassert_winner_metric
)) {
1241 assert_action_a5(ch
);
1245 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1247 int old_atd
= PIM_FORCE_BOOLEAN(
1248 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1250 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1252 if (new_atd
== old_atd
)
1255 if (PIM_DEBUG_PIM_EVENTS
) {
1256 char src_str
[INET_ADDRSTRLEN
];
1257 char grp_str
[INET_ADDRSTRLEN
];
1258 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1259 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1261 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1262 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1263 ch
->interface
->name
, old_atd
, new_atd
);
1267 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1268 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1270 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1271 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1273 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1274 assert_action_a5(ch
);
1280 * If we have a new pim interface, check to
1281 * see if any of the pre-existing channels have
1282 * their upstream out that way and turn on forwarding
1283 * for that ifchannel then.
1285 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1287 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1288 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1289 struct interface
*ifp
;
1291 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1292 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1293 struct pim_ifchannel
*ch
;
1298 if (new_pim_ifp
== loop_pim_ifp
)
1301 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1302 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1303 struct pim_upstream
*up
= ch
->upstream
;
1304 if ((!up
->channel_oil
)
1305 && (up
->rpf
.source_nexthop
1306 .interface
== new_ifp
))
1307 pim_forward_start(ch
);
1314 * Downstream per-interface (S,G,rpt) state machine
1315 * states that we need to move (S,G,rpt) items
1316 * into different states at the start of the
1317 * reception of a *,G join as well, when
1318 * we get End of Message
1320 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1323 struct pim_ifchannel
*child
;
1324 struct listnode
*ch_node
, *nch_node
;
1325 struct pim_instance
*pim
=
1326 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1328 if (PIM_DEBUG_PIM_TRACE
)
1330 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1331 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1332 ch
->sg_str
, eom
, join
);
1336 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1337 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1340 switch (child
->ifjoin_state
) {
1341 case PIM_IFJOIN_NOINFO
:
1342 case PIM_IFJOIN_JOIN
:
1344 case PIM_IFJOIN_PRUNE
:
1346 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1348 case PIM_IFJOIN_PRUNE_PENDING
:
1350 child
->ifjoin_state
=
1351 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1353 case PIM_IFJOIN_PRUNE_TMP
:
1354 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1358 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1359 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1360 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1361 struct pim_upstream
*parent
= child
->upstream
->parent
;
1363 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1364 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1366 if (I_am_RP(pim
, child
->sg
.grp
)) {
1367 pim_channel_add_oif(
1368 child
->upstream
->channel_oil
,
1369 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1370 pim_upstream_switch(pim
, child
->upstream
,
1371 PIM_UPSTREAM_JOINED
);
1372 pim_jp_agg_single_upstream_send(
1373 &child
->upstream
->rpf
, child
->upstream
,
1377 pim_jp_agg_single_upstream_send(&parent
->rpf
,
1380 delete_on_noinfo(child
);
1386 unsigned int pim_ifchannel_hash_key(void *arg
)
1388 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1390 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);