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 pim_ifchannel_compare (struct pim_ifchannel
*ch1
, struct pim_ifchannel
*ch2
)
49 struct pim_interface
*pim_ifp1
;
50 struct pim_interface
*pim_ifp2
;
52 pim_ifp1
= ch1
->interface
->info
;
53 pim_ifp2
= ch2
->interface
->info
;
55 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
58 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
61 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
64 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
67 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
70 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
77 * A (*,G) or a (*,*) is going away
78 * remove the parent pointer from
79 * those pointing at us
82 pim_ifchannel_remove_children (struct pim_ifchannel
*ch
)
84 struct pim_ifchannel
*child
;
89 while (!list_isempty (ch
->sources
))
91 child
= listnode_head (ch
->sources
);
93 listnode_delete (ch
->sources
, child
);
98 * A (*,G) or a (*,*) is being created
99 * find all the children that would point
103 pim_ifchannel_find_new_children (struct pim_ifchannel
*ch
)
105 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
106 struct pim_ifchannel
*child
;
107 struct listnode
*ch_node
;
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 for (ALL_LIST_ELEMENTS_RO (pim_ifp
->pim_ifchannel_list
, ch_node
, child
))
120 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
) &&
121 (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
) &&
125 listnode_add_sort (ch
->sources
, child
);
130 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
132 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
135 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
137 struct pim_interface
*pim_ifp
;
139 pim_ifp
= ch
->interface
->info
;
141 if (ch
->upstream
->channel_oil
)
143 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
144 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
145 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
147 /* SGRpt entry could have empty oil */
148 if (ch
->upstream
->channel_oil
)
149 pim_channel_del_oif (ch
->upstream
->channel_oil
, ch
->interface
, mask
);
150 pim_channel_del_oif (ch
->upstream
->channel_oil
, ch
->interface
, mask
);
152 * Do we have any S,G's that are inheriting?
153 * Nuke from on high too.
155 if (ch
->upstream
->sources
)
157 struct pim_upstream
*child
;
158 struct listnode
*up_node
;
160 for (ALL_LIST_ELEMENTS_RO (ch
->upstream
->sources
, up_node
, child
))
161 pim_channel_del_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
166 * When this channel is removed
167 * we need to find all our children
168 * and make sure our pointers are fixed
170 pim_ifchannel_remove_children (ch
);
173 list_delete (ch
->sources
);
175 listnode_delete(ch
->upstream
->ifchannels
, ch
);
177 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
178 pim_upstream_update_join_desired(ch
->upstream
);
181 pim_upstream_del(ch
->upstream
, __PRETTY_FUNCTION__
);
184 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
185 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
186 THREAD_OFF(ch
->t_ifassert_timer
);
190 listnode_delete (ch
->parent
->sources
, ch
);
194 notice that listnode_delete() can't be moved
195 into pim_ifchannel_free() because the later is
196 called by list_delete_all_node()
198 listnode_delete(pim_ifp
->pim_ifchannel_list
, ch
);
199 hash_release(pim_ifp
->pim_ifchannel_hash
, ch
);
200 listnode_delete(pim_ifchannel_list
, ch
);
202 pim_ifchannel_free(ch
);
206 pim_ifchannel_delete_all (struct interface
*ifp
)
208 struct pim_interface
*pim_ifp
;
209 struct listnode
*ifchannel_node
;
210 struct listnode
*ifchannel_nextnode
;
211 struct pim_ifchannel
*ifchannel
;
217 for (ALL_LIST_ELEMENTS (pim_ifp
->pim_ifchannel_list
, ifchannel_node
,
218 ifchannel_nextnode
, ifchannel
))
220 pim_ifchannel_delete (ifchannel
);
224 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
226 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
&&
227 ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
&&
228 ch
->t_ifjoin_expiry_timer
== NULL
)
229 pim_ifchannel_delete(ch
);
233 void pim_ifchannel_ifjoin_switch(const char *caller
,
234 struct pim_ifchannel
*ch
,
235 enum pim_ifjoin_state new_state
)
237 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
239 if (PIM_DEBUG_PIM_EVENTS
)
240 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
243 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
, ch
->flags
),
244 pim_ifchannel_ifjoin_name (new_state
, 0));
247 if (old_state
== new_state
) {
248 if (PIM_DEBUG_PIM_EVENTS
) {
249 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
250 __PRETTY_FUNCTION__
, caller
, new_state
,
251 pim_ifchannel_ifjoin_name(new_state
, 0));
256 ch
->ifjoin_state
= new_state
;
258 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
260 struct pim_upstream
*up
= ch
->upstream
;
261 struct pim_upstream
*child
;
262 struct listnode
*up_node
;
266 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
)
268 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
270 struct channel_oil
*c_oil
= child
->channel_oil
;
271 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
273 if (PIM_DEBUG_PIM_TRACE
)
274 zlog_debug("%s %s: Prune(S,G)=%s from %s",
275 __FILE__
, __PRETTY_FUNCTION__
,
276 child
->sg_str
, up
->sg_str
);
280 if (!pim_upstream_evaluate_join_desired (child
))
282 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
283 pim_upstream_update_join_desired (child
);
287 * If the S,G has no if channel and the c_oil still
288 * has output here then the *,G was supplying the implied
289 * if channel. So remove it.
290 * I think this is dead code now. is it?
292 if (!ch
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
293 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
296 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
298 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
300 if (PIM_DEBUG_PIM_TRACE
)
301 zlog_debug("%s %s: Join(S,G)=%s from %s",
302 __FILE__
, __PRETTY_FUNCTION__
,
303 child
->sg_str
, up
->sg_str
);
305 if (pim_upstream_evaluate_join_desired (child
))
307 pim_channel_add_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
308 pim_upstream_update_join_desired (child
);
314 /* Transition to/from NOINFO ? */
315 if ((old_state
== PIM_IFJOIN_NOINFO
) ||
316 (new_state
== PIM_IFJOIN_NOINFO
)) {
318 if (PIM_DEBUG_PIM_EVENTS
) {
319 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
320 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN" : "UP"),
321 ch
->sg_str
, ch
->interface
->name
);
325 Record uptime of state transition to/from NOINFO
327 ch
->ifjoin_creation
= pim_time_monotonic_sec();
329 pim_upstream_update_join_desired(ch
->upstream
);
330 pim_ifchannel_update_could_assert(ch
);
331 pim_ifchannel_update_assert_tracking_desired(ch
);
335 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
338 switch (ifjoin_state
) {
339 case PIM_IFJOIN_NOINFO
:
340 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
345 case PIM_IFJOIN_JOIN
:
348 case PIM_IFJOIN_PRUNE
:
351 case PIM_IFJOIN_PRUNE_PENDING
:
354 case PIM_IFJOIN_PRUNE_TMP
:
357 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
362 return "ifjoin_bad_state";
365 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
367 switch (ifassert_state
) {
368 case PIM_IFASSERT_NOINFO
: return "NOINFO";
369 case PIM_IFASSERT_I_AM_WINNER
: return "WINNER";
370 case PIM_IFASSERT_I_AM_LOSER
: return "LOSER";
373 return "ifassert_bad_state";
377 RFC 4601: 4.6.5. Assert State Macros
379 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
380 defaults to Infinity when in the NoInfo state.
382 void reset_ifassert_state(struct pim_ifchannel
*ch
)
384 THREAD_OFF(ch
->t_ifassert_timer
);
386 pim_ifassert_winner_set(ch
,
389 qpim_infinite_assert_metric
);
392 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
393 struct prefix_sg
*sg
)
395 struct pim_interface
*pim_ifp
;
396 struct pim_ifchannel
*ch
;
397 struct pim_ifchannel lookup
;
402 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
404 pim_str_sg_dump (sg
),
410 ch
= hash_lookup (pim_ifp
->pim_ifchannel_hash
, &lookup
);
415 static void ifmembership_set(struct pim_ifchannel
*ch
,
416 enum pim_ifmembership membership
)
418 if (ch
->local_ifmembership
== membership
)
421 if (PIM_DEBUG_PIM_EVENTS
) {
422 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
425 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE" : "NOINFO",
426 ch
->interface
->name
);
429 ch
->local_ifmembership
= membership
;
431 pim_upstream_update_join_desired(ch
->upstream
);
432 pim_ifchannel_update_could_assert(ch
);
433 pim_ifchannel_update_assert_tracking_desired(ch
);
437 void pim_ifchannel_membership_clear(struct interface
*ifp
)
439 struct pim_interface
*pim_ifp
;
440 struct listnode
*ch_node
;
441 struct pim_ifchannel
*ch
;
446 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
447 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
451 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
453 struct pim_interface
*pim_ifp
;
454 struct listnode
*node
;
455 struct listnode
*next_node
;
456 struct pim_ifchannel
*ch
;
461 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
, ch
)) {
462 delete_on_noinfo(ch
);
467 * For a given Interface, if we are given a S,G
468 * Find the *,G (If we have it).
469 * If we are passed a *,G, find the *,* ifchannel
472 static struct pim_ifchannel
*
473 pim_ifchannel_find_parent (struct pim_ifchannel
*ch
)
475 struct prefix_sg parent_sg
= ch
->sg
;
476 struct pim_ifchannel
*parent
= NULL
;
479 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
) &&
480 (parent_sg
.grp
.s_addr
!= INADDR_ANY
))
482 parent_sg
.src
.s_addr
= INADDR_ANY
;
483 parent
= pim_ifchannel_find (ch
->interface
, &parent_sg
);
486 listnode_add (parent
->sources
, ch
);
493 struct pim_ifchannel
*
494 pim_ifchannel_add(struct interface
*ifp
,
495 struct prefix_sg
*sg
, int flags
)
497 struct pim_interface
*pim_ifp
;
498 struct pim_ifchannel
*ch
;
499 struct pim_upstream
*up
;
501 ch
= pim_ifchannel_find(ifp
, sg
);
507 up
= pim_upstream_add(sg
, NULL
, flags
, __PRETTY_FUNCTION__
);
509 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
511 pim_str_sg_dump (sg
), ifp
->name
);
515 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
517 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
519 up
->sg_str
, ifp
->name
);
521 pim_upstream_del (up
, __PRETTY_FUNCTION__
);
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
)
533 ch
->sources
= list_new ();
534 ch
->sources
->cmp
= (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 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
548 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval (ch
);
550 ch
->ifassert_winner
.s_addr
= 0;
553 ch
->t_ifassert_timer
= NULL
;
554 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
555 reset_ifassert_state(ch
);
556 if (pim_macro_ch_could_assert_eval(ch
))
557 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
559 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
561 if (pim_macro_assert_tracking_desired_eval(ch
))
562 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
564 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
567 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
568 ch
= hash_get (pim_ifp
->pim_ifchannel_hash
, ch
, hash_alloc_intern
);
569 listnode_add_sort(pim_ifchannel_list
, ch
);
571 listnode_add_sort(up
->ifchannels
, ch
);
576 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
578 pim_forward_stop(ch
);
579 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
580 delete_on_noinfo(ch
);
583 static int on_ifjoin_expiry_timer(struct thread
*t
)
585 struct pim_ifchannel
*ch
;
589 ch
->t_ifjoin_expiry_timer
= NULL
;
591 ifjoin_to_noinfo(ch
);
592 /* ch may have been deleted */
597 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
599 struct pim_ifchannel
*ch
;
600 int send_prune_echo
; /* boolean */
601 struct interface
*ifp
;
602 struct pim_interface
*pim_ifp
;
606 ch
->t_ifjoin_prune_pending_timer
= NULL
;
608 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
)
610 /* Send PruneEcho(S,G) ? */
613 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
615 //ch->ifjoin_state transition to NOINFO
616 ifjoin_to_noinfo(ch
);
617 /* from here ch may have been deleted */
623 rpf
.source_nexthop
.interface
= ifp
;
624 rpf
.rpf_addr
.u
.prefix4
= pim_ifp
->primary_address
;
625 pim_jp_agg_single_upstream_send(&rpf
, ch
->upstream
, 0);
630 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
631 __PRETTY_FUNCTION__
, pim_str_sg_dump (&ch
->sg
),
632 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
, ch
->flags
));
638 static void check_recv_upstream(int is_join
,
639 struct interface
*recv_ifp
,
640 struct in_addr upstream
,
641 struct prefix_sg
*sg
,
642 uint8_t source_flags
,
645 struct pim_upstream
*up
;
647 /* Upstream (S,G) in Joined state ? */
648 up
= pim_upstream_find(sg
);
651 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
654 /* Upstream (S,G) in Joined state */
656 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
657 /* RPF'(S,G) not found */
658 zlog_warn("%s %s: RPF'%s not found",
659 __FILE__
, __PRETTY_FUNCTION__
,
664 /* upstream directed to RPF'(S,G) ? */
665 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
666 char up_str
[INET_ADDRSTRLEN
];
667 char rpf_str
[PREFIX_STRLEN
];
668 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
669 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
, sizeof(rpf_str
));
670 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
671 __FILE__
, __PRETTY_FUNCTION__
,
673 up_str
, rpf_str
, recv_ifp
->name
);
676 /* upstream directed to RPF'(S,G) */
679 /* Join(S,G) to RPF'(S,G) */
680 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
, holdtime
);
684 /* Prune to RPF'(S,G) */
686 if (source_flags
& PIM_RPT_BIT_MASK
) {
687 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
688 /* Prune(*,G) to RPF'(S,G) */
689 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", up
);
693 /* Prune(S,G,rpt) to RPF'(S,G) */
694 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", up
);
698 /* Prune(S,G) to RPF'(S,G) */
699 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
703 nonlocal_upstream(int is_join
,
704 struct interface
*recv_ifp
,
705 struct in_addr upstream
,
706 struct prefix_sg
*sg
,
707 uint8_t source_flags
,
710 struct pim_interface
*recv_pim_ifp
;
711 int is_local
; /* boolean */
713 recv_pim_ifp
= recv_ifp
->info
;
714 zassert(recv_pim_ifp
);
716 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
721 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
722 char up_str
[INET_ADDRSTRLEN
];
723 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
724 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
726 is_join
? "join" : "prune",
727 pim_str_sg_dump (sg
),
728 up_str
, recv_ifp
->name
);
732 * Since recv upstream addr was not directed to our primary
733 * address, check if we should react to it in any way.
735 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
,
736 source_flags
, holdtime
);
738 return 1; /* non-local */
741 void pim_ifchannel_join_add(struct interface
*ifp
,
742 struct in_addr neigh_addr
,
743 struct in_addr upstream
,
744 struct prefix_sg
*sg
,
745 uint8_t source_flags
,
748 struct pim_interface
*pim_ifp
;
749 struct pim_ifchannel
*ch
;
751 if (nonlocal_upstream(1 /* join */, ifp
, upstream
,
752 sg
, source_flags
, holdtime
)) {
756 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
761 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
763 Transitions from "I am Assert Loser" State
765 Receive Join(S,G) on Interface I
767 We receive a Join(S,G) that has the Upstream Neighbor Address
768 field set to my primary IP address on interface I. The action is
769 to transition to NoInfo state, delete this (S,G) assert state
770 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
773 Notice: The nonlocal_upstream() test above ensures the upstream
774 address of the join message is our primary address.
776 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
777 char neigh_str
[INET_ADDRSTRLEN
];
778 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
, sizeof(neigh_str
));
779 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
781 ch
->sg_str
, neigh_str
, ifp
->name
);
783 assert_action_a5(ch
);
789 switch (ch
->ifjoin_state
) {
790 case PIM_IFJOIN_NOINFO
:
791 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
792 if (pim_macro_chisin_oiflist(ch
)) {
793 pim_upstream_inherited_olist (ch
->upstream
);
794 pim_forward_start(ch
);
797 * If we are going to be a LHR, we need to note it
799 if (ch
->upstream
->parent
&&
800 (ch
->upstream
->parent
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
) &&
801 !(ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_LHR
))
803 pim_upstream_ref (ch
->upstream
, PIM_UPSTREAM_FLAG_MASK_SRC_LHR
);
804 pim_upstream_keep_alive_timer_start (ch
->upstream
, qpim_keep_alive_time
);
807 case PIM_IFJOIN_JOIN
:
808 zassert(!ch
->t_ifjoin_prune_pending_timer
);
811 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
812 previously received join message with holdtime=0xFFFF.
814 if (ch
->t_ifjoin_expiry_timer
) {
815 unsigned long remain
=
816 thread_timer_remain_second(ch
->t_ifjoin_expiry_timer
);
817 if (remain
> holdtime
) {
819 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
821 Transitions from Join State
823 The (S,G) downstream state machine on interface I remains in
824 Join state, and the Expiry Timer (ET) is restarted, set to
825 maximum of its current value and the HoldTime from the
826 triggering Join/Prune message.
828 Conclusion: Do not change the ET if the current value is
829 higher than the received join holdtime.
834 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
836 case PIM_IFJOIN_PRUNE
:
837 if (source_flags
& PIM_ENCODE_RPT_BIT
)
838 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
840 case PIM_IFJOIN_PRUNE_PENDING
:
841 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
842 if (source_flags
& PIM_ENCODE_RPT_BIT
)
844 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
845 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
848 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
850 case PIM_IFJOIN_PRUNE_TMP
:
852 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
856 if (holdtime
!= 0xFFFF) {
857 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
858 &ch
->t_ifjoin_expiry_timer
);
862 void pim_ifchannel_prune(struct interface
*ifp
,
863 struct in_addr upstream
,
864 struct prefix_sg
*sg
,
865 uint8_t source_flags
,
868 struct pim_ifchannel
*ch
;
869 struct pim_interface
*pim_ifp
;
870 int jp_override_interval_msec
;
872 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
,
873 sg
, source_flags
, holdtime
)) {
877 ch
= pim_ifchannel_find (ifp
, sg
);
878 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
))
881 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
882 __PRETTY_FUNCTION__
, ifp
->name
, pim_str_sg_dump (sg
), source_flags
);
886 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
892 switch (ch
->ifjoin_state
) {
893 case PIM_IFJOIN_NOINFO
:
894 if (source_flags
& PIM_ENCODE_RPT_BIT
)
896 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
897 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
898 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
899 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
901 jp_override_interval_msec
= 0; /* schedule to expire immediately */
902 /* If we called ifjoin_prune() directly instead, care should
903 be taken not to use "ch" afterwards since it would be
906 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
907 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
908 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
909 jp_override_interval_msec
,
910 &ch
->t_ifjoin_prune_pending_timer
);
911 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
912 &ch
->t_ifjoin_expiry_timer
);
913 pim_upstream_update_join_desired(ch
->upstream
);
916 case PIM_IFJOIN_PRUNE_PENDING
:
919 case PIM_IFJOIN_JOIN
:
920 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
922 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_PRUNE_PENDING
);
924 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
925 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
927 jp_override_interval_msec
= 0; /* schedule to expire immediately */
928 /* If we called ifjoin_prune() directly instead, care should
929 be taken not to use "ch" afterwards since it would be
931 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
932 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
933 jp_override_interval_msec
,
934 &ch
->t_ifjoin_prune_pending_timer
);
936 case PIM_IFJOIN_PRUNE
:
937 if (source_flags
& PIM_ENCODE_RPT_BIT
)
939 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
940 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
941 &ch
->t_ifjoin_expiry_timer
);
944 case PIM_IFJOIN_PRUNE_TMP
:
945 if (source_flags
& PIM_ENCODE_RPT_BIT
)
947 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
948 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
949 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
950 &ch
->t_ifjoin_expiry_timer
);
953 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
954 if (source_flags
& PIM_ENCODE_RPT_BIT
)
956 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
957 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
958 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
959 &ch
->t_ifjoin_expiry_timer
);
966 pim_ifchannel_local_membership_add(struct interface
*ifp
,
967 struct prefix_sg
*sg
)
969 struct pim_ifchannel
*ch
, *starch
;
970 struct pim_interface
*pim_ifp
;
972 /* PIM enabled on interface? */
976 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
979 /* skip (*,G) ch creation if G is of type SSM */
980 if (sg
->src
.s_addr
== INADDR_ANY
)
982 if (pim_is_grp_ssm (sg
->grp
))
984 if (PIM_DEBUG_PIM_EVENTS
)
985 zlog_debug("%s: local membership (S,G)=%s ignored as group is SSM",
986 __PRETTY_FUNCTION__
, pim_str_sg_dump (sg
));
991 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
996 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
998 if (sg
->src
.s_addr
== INADDR_ANY
)
1000 struct pim_upstream
*up
= pim_upstream_find (sg
);
1001 struct pim_upstream
*child
;
1002 struct listnode
*up_node
;
1006 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
1008 if (PIM_DEBUG_EVENTS
)
1009 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1010 __FILE__
, __PRETTY_FUNCTION__
,
1011 child
->sg_str
, ifp
->name
, up
->sg_str
);
1013 ch
= pim_ifchannel_find (ifp
, &child
->sg
);
1014 if (pim_upstream_evaluate_join_desired_interface (child
, ch
, starch
))
1016 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1017 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
1021 if (pimg
->spt
.switchover
== PIM_SPT_INFINITY
)
1023 if (pimg
->spt
.plist
)
1025 struct prefix_list
*plist
= prefix_list_lookup (AFI_IP
, pimg
->spt
.plist
);
1028 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1029 g
.u
.prefix4
= up
->sg
.grp
;
1031 if (prefix_list_apply (plist
, &g
) == PREFIX_DENY
)
1033 pim_channel_add_oif (up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_IGMP
);
1038 pim_channel_add_oif (up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_IGMP
);
1044 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1045 struct prefix_sg
*sg
)
1047 struct pim_ifchannel
*starch
, *ch
, *orig
;
1048 struct pim_interface
*pim_ifp
;
1050 /* PIM enabled on interface? */
1051 pim_ifp
= ifp
->info
;
1054 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1057 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1061 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1063 if (sg
->src
.s_addr
== INADDR_ANY
)
1065 struct pim_upstream
*up
= pim_upstream_find (sg
);
1066 struct pim_upstream
*child
;
1067 struct listnode
*up_node
, *up_nnode
;
1071 for (ALL_LIST_ELEMENTS (up
->sources
, up_node
, up_nnode
, child
))
1073 struct channel_oil
*c_oil
= child
->channel_oil
;
1074 struct pim_ifchannel
*chchannel
= pim_ifchannel_find (ifp
, &child
->sg
);
1075 struct pim_interface
*pim_ifp
= ifp
->info
;
1077 if (PIM_DEBUG_EVENTS
)
1078 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1079 __FILE__
, __PRETTY_FUNCTION__
,
1080 up
->sg_str
, ifp
->name
, child
->sg_str
);
1082 ch
= pim_ifchannel_find (ifp
, &child
->sg
);
1083 if (c_oil
&& !pim_upstream_evaluate_join_desired_interface (child
, ch
, starch
))
1084 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1087 * If the S,G has no if channel and the c_oil still
1088 * has output here then the *,G was supplying the implied
1089 * if channel. So remove it.
1091 if (!chchannel
&& c_oil
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1092 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1094 /* Child node removal/ref count-- will happen as part of parent' delete_no_info */
1097 delete_on_noinfo(orig
);
1100 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1102 int old_couldassert
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1103 int new_couldassert
= PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1105 if (new_couldassert
== old_couldassert
)
1108 if (PIM_DEBUG_PIM_EVENTS
) {
1109 char src_str
[INET_ADDRSTRLEN
];
1110 char grp_str
[INET_ADDRSTRLEN
];
1111 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1112 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1113 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1114 __PRETTY_FUNCTION__
,
1115 src_str
, grp_str
, ch
->interface
->name
,
1116 old_couldassert
, new_couldassert
);
1119 if (new_couldassert
) {
1120 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1121 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1124 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1125 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1127 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1128 assert_action_a4(ch
);
1132 pim_ifchannel_update_my_assert_metric(ch
);
1136 my_assert_metric may be affected by:
1139 pim_ifp->primary_address
1140 rpf->source_nexthop.mrib_metric_preference;
1141 rpf->source_nexthop.mrib_route_metric;
1143 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1145 struct pim_assert_metric my_metric_new
= pim_macro_ch_my_assert_metric_eval(ch
);
1147 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1150 if (PIM_DEBUG_PIM_EVENTS
) {
1151 char src_str
[INET_ADDRSTRLEN
];
1152 char grp_str
[INET_ADDRSTRLEN
];
1153 char old_addr_str
[INET_ADDRSTRLEN
];
1154 char new_addr_str
[INET_ADDRSTRLEN
];
1155 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1156 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1157 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
, old_addr_str
, sizeof(old_addr_str
));
1158 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
, new_addr_str
, sizeof(new_addr_str
));
1159 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1160 __PRETTY_FUNCTION__
,
1161 src_str
, grp_str
, ch
->interface
->name
,
1162 ch
->ifassert_my_metric
.rpt_bit_flag
,
1163 ch
->ifassert_my_metric
.metric_preference
,
1164 ch
->ifassert_my_metric
.route_metric
,
1166 my_metric_new
.rpt_bit_flag
,
1167 my_metric_new
.metric_preference
,
1168 my_metric_new
.route_metric
,
1172 ch
->ifassert_my_metric
= my_metric_new
;
1174 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1175 &ch
->ifassert_winner_metric
)) {
1176 assert_action_a5(ch
);
1180 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1182 int old_atd
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1183 int new_atd
= PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1185 if (new_atd
== old_atd
)
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: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1194 __PRETTY_FUNCTION__
,
1195 src_str
, grp_str
, ch
->interface
->name
,
1200 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1201 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1204 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1205 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1207 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1208 assert_action_a5(ch
);
1214 * If we have a new pim interface, check to
1215 * see if any of the pre-existing channels have
1216 * their upstream out that way and turn on forwarding
1217 * for that ifchannel then.
1220 pim_ifchannel_scan_forward_start (struct interface
*new_ifp
)
1222 struct listnode
*ifnode
;
1223 struct interface
*ifp
;
1224 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1226 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), ifnode
, ifp
))
1228 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1229 struct listnode
*ch_node
;
1230 struct pim_ifchannel
*ch
;
1235 if (new_pim_ifp
== loop_pim_ifp
)
1238 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp
->pim_ifchannel_list
, ch_node
, ch
))
1240 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
1242 struct pim_upstream
*up
= ch
->upstream
;
1243 if ((!up
->channel_oil
) &&
1244 (up
->rpf
.source_nexthop
.interface
== new_ifp
))
1245 pim_forward_start (ch
);
1252 * Downstream per-interface (S,G,rpt) state machine
1253 * states that we need to move (S,G,rpt) items
1254 * into different states at the start of the
1255 * reception of a *,G join as well, when
1256 * we get End of Message
1259 pim_ifchannel_set_star_g_join_state (struct pim_ifchannel
*ch
, int eom
, uint8_t source_flags
, uint8_t join
)
1261 struct pim_ifchannel
*child
;
1262 struct listnode
*ch_node
;
1264 if (PIM_DEBUG_PIM_TRACE
)
1265 zlog_debug ("%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1266 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1267 ch
->sg_str
, eom
, join
);
1271 for (ALL_LIST_ELEMENTS_RO (ch
->sources
, ch_node
, child
))
1273 /* Only *,G Join received and no (SG-RPT) prune.
1274 Scan all S,G associated to G and if any SG-RPT
1275 remove the SG-RPT flag.
1277 if (join
&& (source_flags
& PIM_RPT_BIT_MASK
) &&
1278 (source_flags
& PIM_WILDCARD_BIT_MASK
))
1280 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1282 struct pim_upstream
*up
= child
->upstream
;
1284 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1287 if (PIM_DEBUG_TRACE
)
1288 zlog_debug ("%s: clearing SGRpt flag, add inherit oif to up %s ", __PRETTY_FUNCTION__
, up
->sg_str
);
1289 pim_channel_add_oif (up
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1293 /* Received SG-RPT Prune delete oif from S,G */
1294 else if (join
== 0 && (source_flags
& PIM_RPT_BIT_MASK
) &&
1295 !(source_flags
& PIM_WILDCARD_BIT_MASK
))
1297 struct pim_upstream
*up
= child
->upstream
;
1299 PIM_IF_FLAG_SET_S_G_RPT(child
->flags
);
1302 if (PIM_DEBUG_TRACE
)
1303 zlog_debug ("%s: del inherit oif from up %s", __PRETTY_FUNCTION__
, up
->sg_str
);
1304 pim_channel_del_oif (up
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1308 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1311 switch (child
->ifjoin_state
)
1313 case PIM_IFJOIN_NOINFO
:
1314 case PIM_IFJOIN_JOIN
:
1316 case PIM_IFJOIN_PRUNE
:
1318 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1320 case PIM_IFJOIN_PRUNE_PENDING
:
1322 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING_TMP
;
1324 case PIM_IFJOIN_PRUNE_TMP
:
1325 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1327 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1334 pim_ifchannel_hash_key (void *arg
)
1336 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1338 return jhash_2words (ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);
1342 pim_ifchannel_equal (const void *arg1
, const void *arg2
)
1344 const struct pim_ifchannel
*ch1
= (const struct pim_ifchannel
*)arg1
;
1345 const struct pim_ifchannel
*ch2
= (const struct pim_ifchannel
*)arg2
;
1347 if ((ch1
->sg
.grp
.s_addr
== ch2
->sg
.grp
.s_addr
) &&
1348 (ch1
->sg
.src
.s_addr
== ch2
->sg
.src
.s_addr
))