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
);
151 * Do we have any S,G's that are inheriting?
152 * Nuke from on high too.
154 if (ch
->upstream
->sources
)
156 struct pim_upstream
*child
;
157 struct listnode
*up_node
;
159 for (ALL_LIST_ELEMENTS_RO (ch
->upstream
->sources
, up_node
, child
))
160 pim_channel_del_oif (child
->channel_oil
, ch
->interface
, 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 (ch
->sources
);
174 listnode_delete(ch
->upstream
->ifchannels
, ch
);
176 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
177 pim_upstream_update_join_desired(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(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
);
193 listnode_delete (ch
->parent
->sources
, ch
);
197 notice that listnode_delete() can't be moved
198 into pim_ifchannel_free() because the later is
199 called by list_delete_all_node()
201 listnode_delete(pim_ifp
->pim_ifchannel_list
, ch
);
202 hash_release(pim_ifp
->pim_ifchannel_hash
, ch
);
203 listnode_delete(pim_ifchannel_list
, ch
);
205 if (PIM_DEBUG_PIM_TRACE
)
206 zlog_debug ("%s: ifchannel entry %s is deleted ", __PRETTY_FUNCTION__
, ch
->sg_str
);
208 pim_ifchannel_free(ch
);
212 pim_ifchannel_delete_all (struct interface
*ifp
)
214 struct pim_interface
*pim_ifp
;
215 struct listnode
*ifchannel_node
;
216 struct listnode
*ifchannel_nextnode
;
217 struct pim_ifchannel
*ifchannel
;
223 for (ALL_LIST_ELEMENTS (pim_ifp
->pim_ifchannel_list
, ifchannel_node
,
224 ifchannel_nextnode
, ifchannel
))
226 pim_ifchannel_delete (ifchannel
);
230 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
232 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
&&
233 ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
&&
234 ch
->t_ifjoin_expiry_timer
== NULL
)
235 pim_ifchannel_delete(ch
);
239 void pim_ifchannel_ifjoin_switch(const char *caller
,
240 struct pim_ifchannel
*ch
,
241 enum pim_ifjoin_state new_state
)
243 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
245 if (PIM_DEBUG_PIM_EVENTS
)
246 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
249 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
, ch
->flags
),
250 pim_ifchannel_ifjoin_name (new_state
, 0));
253 if (old_state
== new_state
) {
254 if (PIM_DEBUG_PIM_EVENTS
) {
255 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
256 __PRETTY_FUNCTION__
, caller
, new_state
,
257 pim_ifchannel_ifjoin_name(new_state
, 0));
262 ch
->ifjoin_state
= new_state
;
264 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
266 struct pim_upstream
*up
= ch
->upstream
;
267 struct pim_upstream
*child
;
268 struct listnode
*up_node
;
272 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
)
274 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
276 struct channel_oil
*c_oil
= child
->channel_oil
;
277 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
279 if (PIM_DEBUG_PIM_TRACE
)
280 zlog_debug("%s %s: Prune(S,G)=%s from %s",
281 __FILE__
, __PRETTY_FUNCTION__
,
282 child
->sg_str
, up
->sg_str
);
286 if (!pim_upstream_evaluate_join_desired (child
))
288 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
289 pim_upstream_update_join_desired (child
);
293 * If the S,G has no if channel and the c_oil still
294 * has output here then the *,G was supplying the implied
295 * if channel. So remove it.
296 * I think this is dead code now. is it?
298 if (!ch
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
299 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
302 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
304 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
306 if (PIM_DEBUG_PIM_TRACE
)
307 zlog_debug("%s %s: Join(S,G)=%s from %s",
308 __FILE__
, __PRETTY_FUNCTION__
,
309 child
->sg_str
, up
->sg_str
);
311 if (pim_upstream_evaluate_join_desired (child
))
313 pim_channel_add_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
314 pim_upstream_update_join_desired (child
);
320 /* Transition to/from NOINFO ? */
321 if ((old_state
== PIM_IFJOIN_NOINFO
) ||
322 (new_state
== PIM_IFJOIN_NOINFO
)) {
324 if (PIM_DEBUG_PIM_EVENTS
) {
325 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
326 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN" : "UP"),
327 ch
->sg_str
, ch
->interface
->name
);
331 Record uptime of state transition to/from NOINFO
333 ch
->ifjoin_creation
= pim_time_monotonic_sec();
335 pim_upstream_update_join_desired(ch
->upstream
);
336 pim_ifchannel_update_could_assert(ch
);
337 pim_ifchannel_update_assert_tracking_desired(ch
);
341 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
344 switch (ifjoin_state
) {
345 case PIM_IFJOIN_NOINFO
:
346 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
351 case PIM_IFJOIN_JOIN
:
354 case PIM_IFJOIN_PRUNE
:
357 case PIM_IFJOIN_PRUNE_PENDING
:
360 case PIM_IFJOIN_PRUNE_TMP
:
363 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
368 return "ifjoin_bad_state";
371 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
373 switch (ifassert_state
) {
374 case PIM_IFASSERT_NOINFO
: return "NOINFO";
375 case PIM_IFASSERT_I_AM_WINNER
: return "WINNER";
376 case PIM_IFASSERT_I_AM_LOSER
: return "LOSER";
379 return "ifassert_bad_state";
383 RFC 4601: 4.6.5. Assert State Macros
385 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
386 defaults to Infinity when in the NoInfo state.
388 void reset_ifassert_state(struct pim_ifchannel
*ch
)
390 struct in_addr any
= { .s_addr
= INADDR_ANY
};
392 THREAD_OFF(ch
->t_ifassert_timer
);
394 pim_ifassert_winner_set(ch
,
397 qpim_infinite_assert_metric
);
400 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
401 struct prefix_sg
*sg
)
403 struct pim_interface
*pim_ifp
;
404 struct pim_ifchannel
*ch
;
405 struct pim_ifchannel lookup
;
410 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
412 pim_str_sg_dump (sg
),
418 ch
= hash_lookup (pim_ifp
->pim_ifchannel_hash
, &lookup
);
423 static void ifmembership_set(struct pim_ifchannel
*ch
,
424 enum pim_ifmembership membership
)
426 if (ch
->local_ifmembership
== membership
)
429 if (PIM_DEBUG_PIM_EVENTS
) {
430 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
433 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE" : "NOINFO",
434 ch
->interface
->name
);
437 ch
->local_ifmembership
= membership
;
439 pim_upstream_update_join_desired(ch
->upstream
);
440 pim_ifchannel_update_could_assert(ch
);
441 pim_ifchannel_update_assert_tracking_desired(ch
);
445 void pim_ifchannel_membership_clear(struct interface
*ifp
)
447 struct pim_interface
*pim_ifp
;
448 struct listnode
*ch_node
;
449 struct pim_ifchannel
*ch
;
454 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
455 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
459 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
461 struct pim_interface
*pim_ifp
;
462 struct listnode
*node
;
463 struct listnode
*next_node
;
464 struct pim_ifchannel
*ch
;
469 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
, ch
)) {
470 delete_on_noinfo(ch
);
475 * For a given Interface, if we are given a S,G
476 * Find the *,G (If we have it).
477 * If we are passed a *,G, find the *,* ifchannel
480 static struct pim_ifchannel
*
481 pim_ifchannel_find_parent (struct pim_ifchannel
*ch
)
483 struct prefix_sg parent_sg
= ch
->sg
;
484 struct pim_ifchannel
*parent
= NULL
;
487 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
) &&
488 (parent_sg
.grp
.s_addr
!= INADDR_ANY
))
490 parent_sg
.src
.s_addr
= INADDR_ANY
;
491 parent
= pim_ifchannel_find (ch
->interface
, &parent_sg
);
494 listnode_add (parent
->sources
, ch
);
501 struct pim_ifchannel
*
502 pim_ifchannel_add(struct interface
*ifp
,
503 struct prefix_sg
*sg
, int flags
)
505 struct pim_interface
*pim_ifp
;
506 struct pim_ifchannel
*ch
;
507 struct pim_upstream
*up
;
509 ch
= pim_ifchannel_find(ifp
, sg
);
515 up
= pim_upstream_add(sg
, NULL
, flags
, __PRETTY_FUNCTION__
);
517 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
519 pim_str_sg_dump (sg
), ifp
->name
);
523 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
525 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
527 up
->sg_str
, ifp
->name
);
529 pim_upstream_del (up
, __PRETTY_FUNCTION__
);
537 pim_str_sg_set (sg
, ch
->sg_str
);
538 ch
->parent
= pim_ifchannel_find_parent (ch
);
539 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
541 ch
->sources
= list_new ();
542 ch
->sources
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
547 pim_ifchannel_find_new_children (ch
);
548 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
550 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
551 ch
->t_ifjoin_expiry_timer
= NULL
;
552 ch
->t_ifjoin_prune_pending_timer
= NULL
;
553 ch
->ifjoin_creation
= 0;
555 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
556 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval (ch
);
558 ch
->ifassert_winner
.s_addr
= 0;
561 ch
->t_ifassert_timer
= NULL
;
562 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
563 reset_ifassert_state(ch
);
564 if (pim_macro_ch_could_assert_eval(ch
))
565 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
567 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
569 if (pim_macro_assert_tracking_desired_eval(ch
))
570 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
572 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
575 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
576 ch
= hash_get (pim_ifp
->pim_ifchannel_hash
, ch
, hash_alloc_intern
);
577 listnode_add_sort(pim_ifchannel_list
, ch
);
579 listnode_add_sort(up
->ifchannels
, ch
);
581 if (PIM_DEBUG_PIM_TRACE
)
582 zlog_debug ("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
, ch
->sg_str
);
587 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
589 pim_forward_stop(ch
);
590 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
592 delete_on_noinfo(ch
);
595 static int on_ifjoin_expiry_timer(struct thread
*t
)
597 struct pim_ifchannel
*ch
;
601 ifjoin_to_noinfo(ch
, true);
602 /* ch may have been deleted */
607 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
609 struct pim_ifchannel
*ch
;
610 int send_prune_echo
; /* boolean */
611 struct interface
*ifp
;
612 struct pim_interface
*pim_ifp
;
616 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
)
618 /* Send PruneEcho(S,G) ? */
621 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
627 rpf
.source_nexthop
.interface
= ifp
;
628 rpf
.rpf_addr
.u
.prefix4
= pim_ifp
->primary_address
;
629 pim_jp_agg_single_upstream_send(&rpf
, ch
->upstream
, 0);
631 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
632 message on RP path upon prune timer expiry.
634 if (PIM_IF_FLAG_TEST_S_G_RPT (ch
->flags
))
637 pim_upstream_update_join_desired(ch
->upstream
);
639 ch->ifjoin_state transition to NOINFO state
640 ch_del is set to 0 for not deleteing from here.
641 Holdtime expiry (ch_del set to 1) delete the entry.
643 ifjoin_to_noinfo(ch
, false);
646 ifjoin_to_noinfo(ch
, true);
647 /* from here ch may have been deleted */
651 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
652 __PRETTY_FUNCTION__
, pim_str_sg_dump (&ch
->sg
),
653 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
, ch
->flags
));
659 static void check_recv_upstream(int is_join
,
660 struct interface
*recv_ifp
,
661 struct in_addr upstream
,
662 struct prefix_sg
*sg
,
663 uint8_t source_flags
,
666 struct pim_upstream
*up
;
668 /* Upstream (S,G) in Joined state ? */
669 up
= pim_upstream_find(sg
);
672 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
675 /* Upstream (S,G) in Joined state */
677 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
678 /* RPF'(S,G) not found */
679 zlog_warn("%s %s: RPF'%s not found",
680 __FILE__
, __PRETTY_FUNCTION__
,
685 /* upstream directed to RPF'(S,G) ? */
686 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
687 char up_str
[INET_ADDRSTRLEN
];
688 char rpf_str
[PREFIX_STRLEN
];
689 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
690 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
, sizeof(rpf_str
));
691 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
692 __FILE__
, __PRETTY_FUNCTION__
,
694 up_str
, rpf_str
, recv_ifp
->name
);
697 /* upstream directed to RPF'(S,G) */
700 /* Join(S,G) to RPF'(S,G) */
701 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
, holdtime
);
705 /* Prune to RPF'(S,G) */
707 if (source_flags
& PIM_RPT_BIT_MASK
) {
708 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
709 /* Prune(*,G) to RPF'(S,G) */
710 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", up
);
714 /* Prune(S,G,rpt) to RPF'(S,G) */
715 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", up
);
719 /* Prune(S,G) to RPF'(S,G) */
720 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
724 nonlocal_upstream(int is_join
,
725 struct interface
*recv_ifp
,
726 struct in_addr upstream
,
727 struct prefix_sg
*sg
,
728 uint8_t source_flags
,
731 struct pim_interface
*recv_pim_ifp
;
732 int is_local
; /* boolean */
734 recv_pim_ifp
= recv_ifp
->info
;
735 zassert(recv_pim_ifp
);
737 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
742 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
743 char up_str
[INET_ADDRSTRLEN
];
744 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
745 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
747 is_join
? "join" : "prune",
748 pim_str_sg_dump (sg
),
749 up_str
, recv_ifp
->name
);
753 * Since recv upstream addr was not directed to our primary
754 * address, check if we should react to it in any way.
756 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
,
757 source_flags
, holdtime
);
759 return 1; /* non-local */
762 void pim_ifchannel_join_add(struct interface
*ifp
,
763 struct in_addr neigh_addr
,
764 struct in_addr upstream
,
765 struct prefix_sg
*sg
,
766 uint8_t source_flags
,
769 struct pim_interface
*pim_ifp
;
770 struct pim_ifchannel
*ch
;
772 if (nonlocal_upstream(1 /* join */, ifp
, upstream
,
773 sg
, source_flags
, holdtime
)) {
777 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
782 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
784 Transitions from "I am Assert Loser" State
786 Receive Join(S,G) on Interface I
788 We receive a Join(S,G) that has the Upstream Neighbor Address
789 field set to my primary IP address on interface I. The action is
790 to transition to NoInfo state, delete this (S,G) assert state
791 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
794 Notice: The nonlocal_upstream() test above ensures the upstream
795 address of the join message is our primary address.
797 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
798 char neigh_str
[INET_ADDRSTRLEN
];
799 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
, sizeof(neigh_str
));
800 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
802 ch
->sg_str
, neigh_str
, ifp
->name
);
804 assert_action_a5(ch
);
810 switch (ch
->ifjoin_state
) {
811 case PIM_IFJOIN_NOINFO
:
812 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
813 if (pim_macro_chisin_oiflist(ch
)) {
814 pim_upstream_inherited_olist (ch
->upstream
);
815 pim_forward_start(ch
);
818 * If we are going to be a LHR, we need to note it
820 if (ch
->upstream
->parent
&&
821 (ch
->upstream
->parent
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
) &&
822 !(ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_LHR
))
824 pim_upstream_ref (ch
->upstream
, PIM_UPSTREAM_FLAG_MASK_SRC_LHR
, __PRETTY_FUNCTION__
);
825 pim_upstream_keep_alive_timer_start (ch
->upstream
, qpim_keep_alive_time
);
828 case PIM_IFJOIN_JOIN
:
829 zassert(!ch
->t_ifjoin_prune_pending_timer
);
832 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
833 previously received join message with holdtime=0xFFFF.
835 if (ch
->t_ifjoin_expiry_timer
) {
836 unsigned long remain
=
837 thread_timer_remain_second(ch
->t_ifjoin_expiry_timer
);
838 if (remain
> holdtime
) {
840 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
842 Transitions from Join State
844 The (S,G) downstream state machine on interface I remains in
845 Join state, and the Expiry Timer (ET) is restarted, set to
846 maximum of its current value and the HoldTime from the
847 triggering Join/Prune message.
849 Conclusion: Do not change the ET if the current value is
850 higher than the received join holdtime.
855 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
857 case PIM_IFJOIN_PRUNE
:
858 if (source_flags
& PIM_ENCODE_RPT_BIT
)
859 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
861 case PIM_IFJOIN_PRUNE_PENDING
:
862 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
863 if (source_flags
& PIM_ENCODE_RPT_BIT
)
865 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
866 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
869 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
871 case PIM_IFJOIN_PRUNE_TMP
:
873 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
877 if (holdtime
!= 0xFFFF) {
878 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
879 &ch
->t_ifjoin_expiry_timer
);
883 void pim_ifchannel_prune(struct interface
*ifp
,
884 struct in_addr upstream
,
885 struct prefix_sg
*sg
,
886 uint8_t source_flags
,
889 struct pim_ifchannel
*ch
;
890 struct pim_interface
*pim_ifp
;
891 int jp_override_interval_msec
;
893 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
,
894 sg
, source_flags
, holdtime
)) {
898 ch
= pim_ifchannel_find (ifp
, sg
);
899 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
))
902 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
903 __PRETTY_FUNCTION__
, ifp
->name
, pim_str_sg_dump (sg
), source_flags
);
907 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
913 switch (ch
->ifjoin_state
) {
914 case PIM_IFJOIN_NOINFO
:
915 if (source_flags
& PIM_ENCODE_RPT_BIT
)
917 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
918 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
920 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
921 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
922 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
924 jp_override_interval_msec
= 0; /* schedule to expire immediately */
925 /* If we called ifjoin_prune() directly instead, care should
926 be taken not to use "ch" afterwards since it would be
929 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
930 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
931 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
932 jp_override_interval_msec
,
933 &ch
->t_ifjoin_prune_pending_timer
);
934 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
935 &ch
->t_ifjoin_expiry_timer
);
936 pim_upstream_update_join_desired(ch
->upstream
);
939 case PIM_IFJOIN_PRUNE_PENDING
:
942 case PIM_IFJOIN_JOIN
:
943 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
945 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_PRUNE_PENDING
);
947 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
948 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
950 jp_override_interval_msec
= 0; /* schedule to expire immediately */
951 /* If we called ifjoin_prune() directly instead, care should
952 be taken not to use "ch" afterwards since it would be
954 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
955 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
956 jp_override_interval_msec
,
957 &ch
->t_ifjoin_prune_pending_timer
);
959 case PIM_IFJOIN_PRUNE
:
960 if (source_flags
& PIM_ENCODE_RPT_BIT
)
962 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
963 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
964 &ch
->t_ifjoin_expiry_timer
);
967 case PIM_IFJOIN_PRUNE_TMP
:
968 if (source_flags
& PIM_ENCODE_RPT_BIT
)
970 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
971 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
972 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
973 &ch
->t_ifjoin_expiry_timer
);
976 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
977 if (source_flags
& PIM_ENCODE_RPT_BIT
)
979 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
980 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
981 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
982 &ch
->t_ifjoin_expiry_timer
);
989 pim_ifchannel_local_membership_add(struct interface
*ifp
,
990 struct prefix_sg
*sg
)
992 struct pim_ifchannel
*ch
, *starch
;
993 struct pim_interface
*pim_ifp
;
995 /* PIM enabled on interface? */
999 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1002 /* skip (*,G) ch creation if G is of type SSM */
1003 if (sg
->src
.s_addr
== INADDR_ANY
)
1005 if (pim_is_grp_ssm (sg
->grp
))
1007 if (PIM_DEBUG_PIM_EVENTS
)
1008 zlog_debug("%s: local membership (S,G)=%s ignored as group is SSM",
1009 __PRETTY_FUNCTION__
, pim_str_sg_dump (sg
));
1014 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1019 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1021 if (sg
->src
.s_addr
== INADDR_ANY
)
1023 struct pim_upstream
*up
= pim_upstream_find (sg
);
1024 struct pim_upstream
*child
;
1025 struct listnode
*up_node
;
1029 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
1031 if (PIM_DEBUG_EVENTS
)
1032 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1033 __FILE__
, __PRETTY_FUNCTION__
,
1034 child
->sg_str
, ifp
->name
, up
->sg_str
);
1036 ch
= pim_ifchannel_find (ifp
, &child
->sg
);
1037 if (pim_upstream_evaluate_join_desired_interface (child
, ch
, starch
))
1039 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1040 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
1044 if (pimg
->spt
.switchover
== PIM_SPT_INFINITY
)
1046 if (pimg
->spt
.plist
)
1048 struct prefix_list
*plist
= prefix_list_lookup (AFI_IP
, pimg
->spt
.plist
);
1051 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1052 g
.u
.prefix4
= up
->sg
.grp
;
1054 if (prefix_list_apply (plist
, &g
) == PREFIX_DENY
)
1056 pim_channel_add_oif (up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_IGMP
);
1061 pim_channel_add_oif (up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_IGMP
);
1067 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1068 struct prefix_sg
*sg
)
1070 struct pim_ifchannel
*starch
, *ch
, *orig
;
1071 struct pim_interface
*pim_ifp
;
1073 /* PIM enabled on interface? */
1074 pim_ifp
= ifp
->info
;
1077 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1080 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1084 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1086 if (sg
->src
.s_addr
== INADDR_ANY
)
1088 struct pim_upstream
*up
= pim_upstream_find (sg
);
1089 struct pim_upstream
*child
;
1090 struct listnode
*up_node
, *up_nnode
;
1094 for (ALL_LIST_ELEMENTS (up
->sources
, up_node
, up_nnode
, child
))
1096 struct channel_oil
*c_oil
= child
->channel_oil
;
1097 struct pim_ifchannel
*chchannel
= pim_ifchannel_find (ifp
, &child
->sg
);
1098 struct pim_interface
*pim_ifp
= ifp
->info
;
1100 if (PIM_DEBUG_EVENTS
)
1101 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1102 __FILE__
, __PRETTY_FUNCTION__
,
1103 up
->sg_str
, ifp
->name
, child
->sg_str
);
1105 ch
= pim_ifchannel_find (ifp
, &child
->sg
);
1106 if (c_oil
&& !pim_upstream_evaluate_join_desired_interface (child
, ch
, starch
))
1107 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1110 * If the S,G has no if channel and the c_oil still
1111 * has output here then the *,G was supplying the implied
1112 * if channel. So remove it.
1114 if (!chchannel
&& c_oil
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1115 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1117 /* Child node removal/ref count-- will happen as part of parent' delete_no_info */
1120 delete_on_noinfo(orig
);
1123 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1125 int old_couldassert
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1126 int new_couldassert
= PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1128 if (new_couldassert
== old_couldassert
)
1131 if (PIM_DEBUG_PIM_EVENTS
) {
1132 char src_str
[INET_ADDRSTRLEN
];
1133 char grp_str
[INET_ADDRSTRLEN
];
1134 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1135 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1136 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1137 __PRETTY_FUNCTION__
,
1138 src_str
, grp_str
, ch
->interface
->name
,
1139 old_couldassert
, new_couldassert
);
1142 if (new_couldassert
) {
1143 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1144 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1147 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1148 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1150 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1151 assert_action_a4(ch
);
1155 pim_ifchannel_update_my_assert_metric(ch
);
1159 my_assert_metric may be affected by:
1162 pim_ifp->primary_address
1163 rpf->source_nexthop.mrib_metric_preference;
1164 rpf->source_nexthop.mrib_route_metric;
1166 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1168 struct pim_assert_metric my_metric_new
= pim_macro_ch_my_assert_metric_eval(ch
);
1170 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1173 if (PIM_DEBUG_PIM_EVENTS
) {
1174 char src_str
[INET_ADDRSTRLEN
];
1175 char grp_str
[INET_ADDRSTRLEN
];
1176 char old_addr_str
[INET_ADDRSTRLEN
];
1177 char new_addr_str
[INET_ADDRSTRLEN
];
1178 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1179 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1180 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
, old_addr_str
, sizeof(old_addr_str
));
1181 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
, new_addr_str
, sizeof(new_addr_str
));
1182 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1183 __PRETTY_FUNCTION__
,
1184 src_str
, grp_str
, ch
->interface
->name
,
1185 ch
->ifassert_my_metric
.rpt_bit_flag
,
1186 ch
->ifassert_my_metric
.metric_preference
,
1187 ch
->ifassert_my_metric
.route_metric
,
1189 my_metric_new
.rpt_bit_flag
,
1190 my_metric_new
.metric_preference
,
1191 my_metric_new
.route_metric
,
1195 ch
->ifassert_my_metric
= my_metric_new
;
1197 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1198 &ch
->ifassert_winner_metric
)) {
1199 assert_action_a5(ch
);
1203 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1205 int old_atd
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1206 int new_atd
= PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1208 if (new_atd
== old_atd
)
1211 if (PIM_DEBUG_PIM_EVENTS
) {
1212 char src_str
[INET_ADDRSTRLEN
];
1213 char grp_str
[INET_ADDRSTRLEN
];
1214 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1215 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1216 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1217 __PRETTY_FUNCTION__
,
1218 src_str
, grp_str
, ch
->interface
->name
,
1223 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1224 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1227 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1228 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1230 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1231 assert_action_a5(ch
);
1237 * If we have a new pim interface, check to
1238 * see if any of the pre-existing channels have
1239 * their upstream out that way and turn on forwarding
1240 * for that ifchannel then.
1243 pim_ifchannel_scan_forward_start (struct interface
*new_ifp
)
1245 struct listnode
*ifnode
;
1246 struct interface
*ifp
;
1247 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1249 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), ifnode
, ifp
))
1251 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1252 struct listnode
*ch_node
;
1253 struct pim_ifchannel
*ch
;
1258 if (new_pim_ifp
== loop_pim_ifp
)
1261 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp
->pim_ifchannel_list
, ch_node
, ch
))
1263 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
1265 struct pim_upstream
*up
= ch
->upstream
;
1266 if ((!up
->channel_oil
) &&
1267 (up
->rpf
.source_nexthop
.interface
== new_ifp
))
1268 pim_forward_start (ch
);
1275 * Downstream per-interface (S,G,rpt) state machine
1276 * states that we need to move (S,G,rpt) items
1277 * into different states at the start of the
1278 * reception of a *,G join as well, when
1279 * we get End of Message
1282 pim_ifchannel_set_star_g_join_state (struct pim_ifchannel
*ch
, int eom
, uint8_t source_flags
, uint8_t join
)
1284 struct pim_ifchannel
*child
;
1285 struct listnode
*ch_node
;
1287 if (PIM_DEBUG_PIM_TRACE
)
1288 zlog_debug ("%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1289 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1290 ch
->sg_str
, eom
, join
);
1294 for (ALL_LIST_ELEMENTS_RO (ch
->sources
, ch_node
, child
))
1296 /* Only *,G Join received and no (SG-RPT) prune.
1297 Scan all S,G associated to G and if any SG-RPT
1298 remove the SG-RPT flag.
1300 if (join
&& (source_flags
& PIM_RPT_BIT_MASK
) &&
1301 (source_flags
& PIM_WILDCARD_BIT_MASK
))
1303 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1305 struct pim_upstream
*up
= child
->upstream
;
1307 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1310 if (PIM_DEBUG_TRACE
)
1311 zlog_debug ("%s: clearing SGRpt flag, add inherit oif to up %s ", __PRETTY_FUNCTION__
, up
->sg_str
);
1312 pim_channel_add_oif (up
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1316 /* Received SG-RPT Prune delete oif from S,G */
1317 else if (join
== 0 && (source_flags
& PIM_RPT_BIT_MASK
) &&
1318 !(source_flags
& PIM_WILDCARD_BIT_MASK
))
1320 struct pim_upstream
*up
= child
->upstream
;
1322 PIM_IF_FLAG_SET_S_G_RPT(child
->flags
);
1325 if (PIM_DEBUG_TRACE
)
1326 zlog_debug ("%s: SGRpt Set, del inherit oif from up %s", __PRETTY_FUNCTION__
, up
->sg_str
);
1327 pim_channel_del_oif (up
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1331 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1334 switch (child
->ifjoin_state
)
1336 case PIM_IFJOIN_NOINFO
:
1337 case PIM_IFJOIN_JOIN
:
1339 case PIM_IFJOIN_PRUNE
:
1341 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1343 case PIM_IFJOIN_PRUNE_PENDING
:
1345 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING_TMP
;
1347 case PIM_IFJOIN_PRUNE_TMP
:
1348 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1350 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1357 pim_ifchannel_hash_key (void *arg
)
1359 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1361 return jhash_2words (ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);
1365 pim_ifchannel_equal (const void *arg1
, const void *arg2
)
1367 const struct pim_ifchannel
*ch1
= (const struct pim_ifchannel
*)arg1
;
1368 const struct pim_ifchannel
*ch2
= (const struct pim_ifchannel
*)arg2
;
1370 if ((ch1
->sg
.grp
.s_addr
== ch2
->sg
.grp
.s_addr
) &&
1371 (ch1
->sg
.src
.s_addr
== ch2
->sg
.src
.s_addr
))