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 (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
215 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
217 pim_ifchannel_delete(ch
);
221 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
223 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
224 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
225 && ch
->t_ifjoin_expiry_timer
== NULL
)
226 pim_ifchannel_delete(ch
);
229 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
230 enum pim_ifjoin_state new_state
)
232 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
233 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
235 if (PIM_DEBUG_PIM_EVENTS
)
237 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
238 ch
->interface
->name
, ch
->sg_str
,
239 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
240 pim_ifchannel_ifjoin_name(new_state
, 0));
243 if (old_state
== new_state
) {
244 if (PIM_DEBUG_PIM_EVENTS
) {
246 "%s calledby %s: non-transition on state %d (%s)",
247 __PRETTY_FUNCTION__
, caller
, new_state
,
248 pim_ifchannel_ifjoin_name(new_state
, 0));
253 ch
->ifjoin_state
= new_state
;
255 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
256 struct pim_upstream
*up
= ch
->upstream
;
257 struct pim_upstream
*child
;
258 struct listnode
*up_node
;
261 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
262 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
264 struct channel_oil
*c_oil
=
267 if (PIM_DEBUG_PIM_TRACE
)
269 "%s %s: Prune(S,G)=%s from %s",
277 if (!pim_upstream_evaluate_join_desired(
278 pim_ifp
->pim
, child
)) {
280 c_oil
, ch
->interface
,
281 PIM_OIF_FLAG_PROTO_STAR
);
282 pim_upstream_update_join_desired(
283 pim_ifp
->pim
, child
);
287 * If the S,G has no if channel and the
289 * has output here then the *,G was
290 * supplying the implied
291 * if channel. So remove it.
292 * I think this is dead code now. is it?
294 if (c_oil
->oil
.mfcc_ttls
295 [pim_ifp
->mroute_vif_index
])
297 c_oil
, ch
->interface
,
298 PIM_OIF_FLAG_PROTO_STAR
);
301 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
302 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
304 if (PIM_DEBUG_PIM_TRACE
)
306 "%s %s: Join(S,G)=%s from %s",
312 if (pim_upstream_evaluate_join_desired(
313 pim_ifp
->pim
, child
)) {
317 PIM_OIF_FLAG_PROTO_STAR
);
318 pim_upstream_update_join_desired(
319 pim_ifp
->pim
, child
);
325 /* Transition to/from NOINFO ? */
326 if ((old_state
== PIM_IFJOIN_NOINFO
)
327 || (new_state
== PIM_IFJOIN_NOINFO
)) {
329 if (PIM_DEBUG_PIM_EVENTS
) {
330 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
331 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
333 ch
->sg_str
, ch
->interface
->name
);
337 Record uptime of state transition to/from NOINFO
339 ch
->ifjoin_creation
= pim_time_monotonic_sec();
341 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
342 pim_ifchannel_update_could_assert(ch
);
343 pim_ifchannel_update_assert_tracking_desired(ch
);
347 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
350 switch (ifjoin_state
) {
351 case PIM_IFJOIN_NOINFO
:
352 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
357 case PIM_IFJOIN_JOIN
:
360 case PIM_IFJOIN_PRUNE
:
361 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
366 case PIM_IFJOIN_PRUNE_PENDING
:
367 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
372 case PIM_IFJOIN_PRUNE_TMP
:
373 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
378 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
379 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
386 return "ifjoin_bad_state";
389 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
391 switch (ifassert_state
) {
392 case PIM_IFASSERT_NOINFO
:
394 case PIM_IFASSERT_I_AM_WINNER
:
396 case PIM_IFASSERT_I_AM_LOSER
:
400 return "ifassert_bad_state";
404 RFC 4601: 4.6.5. Assert State Macros
406 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
407 defaults to Infinity when in the NoInfo state.
409 void reset_ifassert_state(struct pim_ifchannel
*ch
)
411 struct in_addr any
= {.s_addr
= INADDR_ANY
};
413 THREAD_OFF(ch
->t_ifassert_timer
);
415 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
416 qpim_infinite_assert_metric
);
419 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
420 struct prefix_sg
*sg
)
422 struct pim_interface
*pim_ifp
;
423 struct pim_ifchannel
*ch
;
424 struct pim_ifchannel lookup
;
429 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
430 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
435 lookup
.interface
= ifp
;
436 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
441 static void ifmembership_set(struct pim_ifchannel
*ch
,
442 enum pim_ifmembership membership
)
444 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
446 if (ch
->local_ifmembership
== membership
)
449 if (PIM_DEBUG_PIM_EVENTS
) {
450 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
451 __PRETTY_FUNCTION__
, ch
->sg_str
,
452 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
454 ch
->interface
->name
);
457 ch
->local_ifmembership
= membership
;
459 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
460 pim_ifchannel_update_could_assert(ch
);
461 pim_ifchannel_update_assert_tracking_desired(ch
);
465 void pim_ifchannel_membership_clear(struct interface
*ifp
)
467 struct pim_interface
*pim_ifp
;
468 struct pim_ifchannel
*ch
;
473 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
474 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
477 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
479 struct pim_interface
*pim_ifp
;
480 struct pim_ifchannel
*ch
, *ch_tmp
;
485 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
486 delete_on_noinfo(ch
);
490 * For a given Interface, if we are given a S,G
491 * Find the *,G (If we have it).
492 * If we are passed a *,G, find the *,* ifchannel
495 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
497 struct prefix_sg parent_sg
= ch
->sg
;
498 struct pim_ifchannel
*parent
= NULL
;
501 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
502 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
503 parent_sg
.src
.s_addr
= INADDR_ANY
;
504 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
507 listnode_add(parent
->sources
, ch
);
514 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
515 struct prefix_sg
*sg
,
516 uint8_t source_flags
, int up_flags
)
518 struct pim_interface
*pim_ifp
;
519 struct pim_ifchannel
*ch
;
520 struct pim_upstream
*up
;
522 ch
= pim_ifchannel_find(ifp
, sg
);
528 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
531 "%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
532 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
537 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
538 && !(source_flags
& PIM_ENCODE_WC_BIT
))
539 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
543 pim_str_sg_set(sg
, ch
->sg_str
);
544 ch
->parent
= pim_ifchannel_find_parent(ch
);
545 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
546 ch
->sources
= list_new();
548 (int (*)(void *, void *))pim_ifchannel_compare
;
552 pim_ifchannel_find_new_children(ch
);
553 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
555 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
556 ch
->t_ifjoin_expiry_timer
= NULL
;
557 ch
->t_ifjoin_prune_pending_timer
= NULL
;
558 ch
->ifjoin_creation
= 0;
560 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
562 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
,
563 __PRETTY_FUNCTION__
, ch
);
567 "%s: could not attach upstream (S,G)=%s on interface %s",
568 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
571 listnode_delete(ch
->parent
->sources
, ch
);
573 pim_ifchannel_remove_children(ch
);
575 list_delete_and_null(&ch
->sources
);
577 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
578 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
579 THREAD_OFF(ch
->t_ifassert_timer
);
581 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
582 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
587 listnode_add_sort(up
->ifchannels
, ch
);
589 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
590 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
592 ch
->ifassert_winner
.s_addr
= 0;
595 ch
->t_ifassert_timer
= NULL
;
596 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
597 reset_ifassert_state(ch
);
598 if (pim_macro_ch_could_assert_eval(ch
))
599 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
601 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
603 if (pim_macro_assert_tracking_desired_eval(ch
))
604 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
606 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
608 if (PIM_DEBUG_PIM_TRACE
)
609 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
615 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
617 pim_forward_stop(ch
, !ch_del
);
618 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
620 delete_on_noinfo(ch
);
623 static int on_ifjoin_expiry_timer(struct thread
*t
)
625 struct pim_ifchannel
*ch
;
629 ifjoin_to_noinfo(ch
, true);
630 /* ch may have been deleted */
635 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
637 struct pim_ifchannel
*ch
;
638 int send_prune_echo
; /* boolean */
639 struct interface
*ifp
;
640 struct pim_interface
*pim_ifp
;
645 zlog_debug("%s: IFCHANNEL%s %s Prune Pending Timer Popped",
647 pim_str_sg_dump(&ch
->sg
),
648 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
,
651 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
654 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
655 /* Send PruneEcho(S,G) ? */
657 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
659 if (send_prune_echo
) {
662 rpf
.source_nexthop
.interface
= ifp
;
663 rpf
.rpf_addr
.u
.prefix4
=
664 pim_ifp
->primary_address
;
665 pim_jp_agg_single_upstream_send(&rpf
,
670 ifjoin_to_noinfo(ch
, true);
672 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
673 * message on RP path upon prune timer expiry.
675 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
677 struct pim_upstream
*parent
=
678 ch
->upstream
->parent
;
680 pim_upstream_update_join_desired(pim_ifp
->pim
,
683 pim_jp_agg_single_upstream_send(&parent
->rpf
,
688 /* from here ch may have been deleted */
694 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
695 struct in_addr upstream
, struct prefix_sg
*sg
,
696 uint8_t source_flags
, int holdtime
)
698 struct pim_upstream
*up
;
699 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
701 /* Upstream (S,G) in Joined state ? */
702 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
705 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
708 /* Upstream (S,G) in Joined state */
710 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
711 /* RPF'(S,G) not found */
712 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
713 __PRETTY_FUNCTION__
, up
->sg_str
);
717 /* upstream directed to RPF'(S,G) ? */
718 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
719 char up_str
[INET_ADDRSTRLEN
];
720 char rpf_str
[PREFIX_STRLEN
];
721 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
722 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
725 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
726 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
727 rpf_str
, recv_ifp
->name
);
730 /* upstream directed to RPF'(S,G) */
733 /* Join(S,G) to RPF'(S,G) */
734 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
739 /* Prune to RPF'(S,G) */
741 if (source_flags
& PIM_RPT_BIT_MASK
) {
742 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
743 /* Prune(*,G) to RPF'(S,G) */
744 pim_upstream_join_timer_decrease_to_t_override(
749 /* Prune(S,G,rpt) to RPF'(S,G) */
750 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
755 /* Prune(S,G) to RPF'(S,G) */
756 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
759 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
760 struct in_addr upstream
, struct prefix_sg
*sg
,
761 uint8_t source_flags
, uint16_t holdtime
)
763 struct pim_interface
*recv_pim_ifp
;
764 int is_local
; /* boolean */
766 recv_pim_ifp
= recv_ifp
->info
;
767 zassert(recv_pim_ifp
);
769 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
774 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
775 char up_str
[INET_ADDRSTRLEN
];
776 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
777 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
778 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
779 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
783 * Since recv upstream addr was not directed to our primary
784 * address, check if we should react to it in any way.
786 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
789 return 1; /* non-local */
792 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
793 struct in_addr upstream
, struct prefix_sg
*sg
,
794 uint8_t source_flags
, uint16_t holdtime
)
796 struct pim_interface
*pim_ifp
;
797 struct pim_ifchannel
*ch
;
799 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
804 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
805 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
810 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
812 Transitions from "I am Assert Loser" State
814 Receive Join(S,G) on Interface I
816 We receive a Join(S,G) that has the Upstream Neighbor Address
817 field set to my primary IP address on interface I. The action is
818 to transition to NoInfo state, delete this (S,G) assert state
819 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
822 Notice: The nonlocal_upstream() test above ensures the upstream
823 address of the join message is our primary address.
825 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
826 char neigh_str
[INET_ADDRSTRLEN
];
827 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
829 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
830 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
833 assert_action_a5(ch
);
839 switch (ch
->ifjoin_state
) {
840 case PIM_IFJOIN_NOINFO
:
841 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
843 if (pim_macro_chisin_oiflist(ch
)) {
844 pim_upstream_inherited_olist(pim_ifp
->pim
,
846 pim_forward_start(ch
);
849 * If we are going to be a LHR, we need to note it
851 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
852 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
853 && !(ch
->upstream
->flags
854 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
855 pim_upstream_ref(ch
->upstream
,
856 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
857 __PRETTY_FUNCTION__
);
858 pim_upstream_keep_alive_timer_start(
859 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
862 case PIM_IFJOIN_JOIN
:
863 zassert(!ch
->t_ifjoin_prune_pending_timer
);
866 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
868 previously received join message with holdtime=0xFFFF.
870 if (ch
->t_ifjoin_expiry_timer
) {
871 unsigned long remain
= thread_timer_remain_second(
872 ch
->t_ifjoin_expiry_timer
);
873 if (remain
> holdtime
) {
875 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
878 Transitions from Join State
880 The (S,G) downstream state machine on
881 interface I remains in
882 Join state, and the Expiry Timer (ET) is
884 maximum of its current value and the HoldTime
886 triggering Join/Prune message.
888 Conclusion: Do not change the ET if the
890 higher than the received join holdtime.
895 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
897 case PIM_IFJOIN_PRUNE
:
898 if (source_flags
& PIM_ENCODE_RPT_BIT
)
899 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
902 case PIM_IFJOIN_PRUNE_PENDING
:
903 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
904 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
905 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
906 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
909 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
912 case PIM_IFJOIN_PRUNE_TMP
:
914 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
918 if (holdtime
!= 0xFFFF) {
919 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
920 &ch
->t_ifjoin_expiry_timer
);
924 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
925 struct prefix_sg
*sg
, uint8_t source_flags
,
928 struct pim_ifchannel
*ch
;
929 struct pim_interface
*pim_ifp
;
930 int jp_override_interval_msec
;
932 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
937 ch
= pim_ifchannel_find(ifp
, sg
);
938 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
941 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
942 __PRETTY_FUNCTION__
, ifp
->name
,
943 pim_str_sg_dump(sg
), source_flags
);
947 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
948 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
954 switch (ch
->ifjoin_state
) {
955 case PIM_IFJOIN_NOINFO
:
956 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
957 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
958 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
960 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
961 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
962 jp_override_interval_msec
=
963 pim_if_jp_override_interval_msec(ifp
);
965 jp_override_interval_msec
=
966 0; /* schedule to expire immediately */
967 /* If we called ifjoin_prune() directly instead, care
969 be taken not to use "ch" afterwards since it would be
972 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
973 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
974 thread_add_timer_msec(
975 master
, on_ifjoin_prune_pending_timer
, ch
,
976 jp_override_interval_msec
,
977 &ch
->t_ifjoin_prune_pending_timer
);
978 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
979 holdtime
, &ch
->t_ifjoin_expiry_timer
);
980 pim_upstream_update_join_desired(pim_ifp
->pim
,
984 case PIM_IFJOIN_PRUNE_PENDING
:
987 case PIM_IFJOIN_JOIN
:
988 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
990 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
991 PIM_IFJOIN_PRUNE_PENDING
);
993 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
994 jp_override_interval_msec
=
995 pim_if_jp_override_interval_msec(ifp
);
997 jp_override_interval_msec
=
998 0; /* schedule to expire immediately */
999 /* If we called ifjoin_prune() directly instead, care should
1000 be taken not to use "ch" afterwards since it would be
1002 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1003 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
1004 jp_override_interval_msec
,
1005 &ch
->t_ifjoin_prune_pending_timer
);
1007 case PIM_IFJOIN_PRUNE
:
1008 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1009 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1010 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1011 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1014 case PIM_IFJOIN_PRUNE_TMP
:
1015 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1016 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1017 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1018 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1019 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1022 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1023 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1024 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1025 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1026 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
1027 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1033 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1034 struct prefix_sg
*sg
)
1036 struct pim_ifchannel
*ch
, *starch
;
1037 struct pim_interface
*pim_ifp
;
1038 struct pim_instance
*pim
;
1040 /* PIM enabled on interface? */
1041 pim_ifp
= ifp
->info
;
1044 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1049 /* skip (*,G) ch creation if G is of type SSM */
1050 if (sg
->src
.s_addr
== INADDR_ANY
) {
1051 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1052 if (PIM_DEBUG_PIM_EVENTS
)
1054 "%s: local membership (S,G)=%s ignored as group is SSM",
1055 __PRETTY_FUNCTION__
,
1056 pim_str_sg_dump(sg
));
1061 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1066 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1068 if (sg
->src
.s_addr
== INADDR_ANY
) {
1069 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1070 struct pim_upstream
*child
;
1071 struct listnode
*up_node
;
1075 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1076 if (PIM_DEBUG_EVENTS
)
1077 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1078 __FILE__
, __PRETTY_FUNCTION__
,
1079 child
->sg_str
, ifp
->name
,
1082 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1083 if (pim_upstream_evaluate_join_desired_interface(
1084 child
, ch
, starch
)) {
1085 pim_channel_add_oif(child
->channel_oil
, ifp
,
1086 PIM_OIF_FLAG_PROTO_STAR
);
1087 pim_upstream_switch(pim
, child
,
1088 PIM_UPSTREAM_JOINED
);
1092 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1093 if (pim
->spt
.plist
) {
1094 struct prefix_list
*plist
= prefix_list_lookup(
1095 AFI_IP
, pim
->spt
.plist
);
1098 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1099 g
.u
.prefix4
= up
->sg
.grp
;
1101 if (prefix_list_apply(plist
, &g
)
1103 pim_channel_add_oif(
1104 up
->channel_oil
, pim
->regiface
,
1105 PIM_OIF_FLAG_PROTO_IGMP
);
1109 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1110 PIM_OIF_FLAG_PROTO_IGMP
);
1116 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1117 struct prefix_sg
*sg
)
1119 struct pim_ifchannel
*starch
, *ch
, *orig
;
1120 struct pim_interface
*pim_ifp
;
1122 /* PIM enabled on interface? */
1123 pim_ifp
= ifp
->info
;
1126 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1129 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1132 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1134 if (sg
->src
.s_addr
== INADDR_ANY
) {
1135 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1136 struct pim_upstream
*child
;
1137 struct listnode
*up_node
, *up_nnode
;
1141 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1142 struct channel_oil
*c_oil
= child
->channel_oil
;
1143 struct pim_ifchannel
*chchannel
=
1144 pim_ifchannel_find(ifp
, &child
->sg
);
1145 struct pim_interface
*pim_ifp
= ifp
->info
;
1147 if (PIM_DEBUG_EVENTS
)
1148 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1149 __FILE__
, __PRETTY_FUNCTION__
,
1150 up
->sg_str
, ifp
->name
,
1153 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1155 && !pim_upstream_evaluate_join_desired_interface(
1157 pim_channel_del_oif(c_oil
, ifp
,
1158 PIM_OIF_FLAG_PROTO_STAR
);
1161 * If the S,G has no if channel and the c_oil still
1162 * has output here then the *,G was supplying the
1164 * if channel. So remove it.
1166 if (!chchannel
&& c_oil
1167 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1168 pim_channel_del_oif(c_oil
, ifp
,
1169 PIM_OIF_FLAG_PROTO_STAR
);
1171 /* Child node removal/ref count-- will happen as part of
1172 * parent' delete_no_info */
1175 delete_on_noinfo(orig
);
1178 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1180 int old_couldassert
=
1181 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1182 int new_couldassert
=
1183 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1185 if (new_couldassert
== old_couldassert
)
1188 if (PIM_DEBUG_PIM_EVENTS
) {
1189 char src_str
[INET_ADDRSTRLEN
];
1190 char grp_str
[INET_ADDRSTRLEN
];
1191 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1192 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1193 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1194 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1195 ch
->interface
->name
, old_couldassert
,
1199 if (new_couldassert
) {
1200 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1201 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1203 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1204 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1206 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1207 assert_action_a4(ch
);
1211 pim_ifchannel_update_my_assert_metric(ch
);
1215 my_assert_metric may be affected by:
1218 pim_ifp->primary_address
1219 rpf->source_nexthop.mrib_metric_preference;
1220 rpf->source_nexthop.mrib_route_metric;
1222 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1224 struct pim_assert_metric my_metric_new
=
1225 pim_macro_ch_my_assert_metric_eval(ch
);
1227 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1230 if (PIM_DEBUG_PIM_EVENTS
) {
1231 char src_str
[INET_ADDRSTRLEN
];
1232 char grp_str
[INET_ADDRSTRLEN
];
1233 char old_addr_str
[INET_ADDRSTRLEN
];
1234 char new_addr_str
[INET_ADDRSTRLEN
];
1235 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1236 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1237 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1238 old_addr_str
, sizeof(old_addr_str
));
1239 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1240 new_addr_str
, sizeof(new_addr_str
));
1242 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1243 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1244 ch
->interface
->name
,
1245 ch
->ifassert_my_metric
.rpt_bit_flag
,
1246 ch
->ifassert_my_metric
.metric_preference
,
1247 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1248 my_metric_new
.rpt_bit_flag
,
1249 my_metric_new
.metric_preference
,
1250 my_metric_new
.route_metric
, new_addr_str
);
1253 ch
->ifassert_my_metric
= my_metric_new
;
1255 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1256 &ch
->ifassert_winner_metric
)) {
1257 assert_action_a5(ch
);
1261 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1263 int old_atd
= PIM_FORCE_BOOLEAN(
1264 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1266 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1268 if (new_atd
== old_atd
)
1271 if (PIM_DEBUG_PIM_EVENTS
) {
1272 char src_str
[INET_ADDRSTRLEN
];
1273 char grp_str
[INET_ADDRSTRLEN
];
1274 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1275 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1277 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1278 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1279 ch
->interface
->name
, old_atd
, new_atd
);
1283 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1284 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1286 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1287 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1289 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1290 assert_action_a5(ch
);
1296 * If we have a new pim interface, check to
1297 * see if any of the pre-existing channels have
1298 * their upstream out that way and turn on forwarding
1299 * for that ifchannel then.
1301 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1303 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1304 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1305 struct interface
*ifp
;
1307 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1308 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1309 struct pim_ifchannel
*ch
;
1314 if (new_pim_ifp
== loop_pim_ifp
)
1317 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1318 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1319 struct pim_upstream
*up
= ch
->upstream
;
1320 if ((!up
->channel_oil
)
1321 && (up
->rpf
.source_nexthop
1322 .interface
== new_ifp
))
1323 pim_forward_start(ch
);
1330 * Downstream per-interface (S,G,rpt) state machine
1331 * states that we need to move (S,G,rpt) items
1332 * into different states at the start of the
1333 * reception of a *,G join as well, when
1334 * we get End of Message
1336 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1339 struct pim_ifchannel
*child
;
1340 struct listnode
*ch_node
, *nch_node
;
1341 struct pim_instance
*pim
=
1342 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1344 if (PIM_DEBUG_PIM_TRACE
)
1346 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1347 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1348 ch
->sg_str
, eom
, join
);
1352 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1353 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1356 switch (child
->ifjoin_state
) {
1357 case PIM_IFJOIN_NOINFO
:
1358 case PIM_IFJOIN_JOIN
:
1360 case PIM_IFJOIN_PRUNE
:
1362 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1364 case PIM_IFJOIN_PRUNE_PENDING
:
1366 child
->ifjoin_state
=
1367 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1369 case PIM_IFJOIN_PRUNE_TMP
:
1370 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1374 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1375 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1376 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1377 struct pim_upstream
*parent
=
1378 child
->upstream
->parent
;
1380 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1381 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1383 if (I_am_RP(pim
, child
->sg
.grp
)) {
1384 pim_channel_add_oif(
1385 child
->upstream
->channel_oil
,
1387 PIM_OIF_FLAG_PROTO_STAR
);
1388 pim_upstream_switch(
1389 pim
, child
->upstream
,
1390 PIM_UPSTREAM_JOINED
);
1391 pim_jp_agg_single_upstream_send(
1392 &child
->upstream
->rpf
,
1393 child
->upstream
, true);
1396 pim_jp_agg_single_upstream_send(
1400 delete_on_noinfo(child
);
1406 unsigned int pim_ifchannel_hash_key(void *arg
)
1408 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1410 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);