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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
34 #include "pim_iface.h"
35 #include "pim_ifchannel.h"
36 #include "pim_zebra.h"
42 #include "pim_macro.h"
44 #include "pim_upstream.h"
48 pim_ifchannel_compare (struct pim_ifchannel
*ch1
, struct pim_ifchannel
*ch2
)
50 struct pim_interface
*pim_ifp1
;
51 struct pim_interface
*pim_ifp2
;
53 pim_ifp1
= ch1
->interface
->info
;
54 pim_ifp2
= ch2
->interface
->info
;
56 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
59 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
62 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
65 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
68 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
71 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
78 * A (*,G) or a (*,*) is going away
79 * remove the parent pointer from
80 * those pointing at us
83 pim_ifchannel_remove_children (struct pim_ifchannel
*ch
)
85 struct pim_ifchannel
*child
;
90 while (!list_isempty (ch
->sources
))
92 child
= listnode_head (ch
->sources
);
94 listnode_delete (ch
->sources
, child
);
99 * A (*,G) or a (*,*) is being created
100 * find all the children that would point
104 pim_ifchannel_find_new_children (struct pim_ifchannel
*ch
)
106 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
107 struct pim_ifchannel
*child
;
108 struct listnode
*ch_node
;
110 // Basic Sanity that we are not being silly
111 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
112 (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
115 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
) &&
116 (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
119 for (ALL_LIST_ELEMENTS_RO (pim_ifp
->pim_ifchannel_list
, ch_node
, child
))
121 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
) &&
122 (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
) &&
126 listnode_add_sort (ch
->sources
, child
);
131 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
133 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
136 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
138 struct pim_interface
*pim_ifp
;
140 pim_ifp
= ch
->interface
->info
;
142 if (ch
->upstream
->channel_oil
)
144 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
145 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
146 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
148 /* SGRpt entry could have empty oil */
149 if (ch
->upstream
->channel_oil
)
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 /* upstream is common across ifchannels, check if upstream's
182 ifchannel list is empty before deleting upstream_del
183 ref count will take care of it.
185 pim_upstream_del(ch
->upstream
, __PRETTY_FUNCTION__
);
188 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
189 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
190 THREAD_OFF(ch
->t_ifassert_timer
);
194 listnode_delete (ch
->parent
->sources
, ch
);
198 notice that listnode_delete() can't be moved
199 into pim_ifchannel_free() because the later is
200 called by list_delete_all_node()
202 listnode_delete(pim_ifp
->pim_ifchannel_list
, ch
);
203 hash_release(pim_ifp
->pim_ifchannel_hash
, ch
);
204 listnode_delete(pim_ifchannel_list
, ch
);
206 if (PIM_DEBUG_PIM_TRACE
)
207 zlog_debug ("%s: ifchannel entry %s is deleted ", __PRETTY_FUNCTION__
, ch
->sg_str
);
209 pim_ifchannel_free(ch
);
213 pim_ifchannel_delete_all (struct interface
*ifp
)
215 struct pim_interface
*pim_ifp
;
216 struct listnode
*ifchannel_node
;
217 struct listnode
*ifchannel_nextnode
;
218 struct pim_ifchannel
*ifchannel
;
224 for (ALL_LIST_ELEMENTS (pim_ifp
->pim_ifchannel_list
, ifchannel_node
,
225 ifchannel_nextnode
, ifchannel
))
227 pim_ifchannel_delete (ifchannel
);
231 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
233 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
&&
234 ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
&&
235 ch
->t_ifjoin_expiry_timer
== NULL
)
236 pim_ifchannel_delete(ch
);
240 void pim_ifchannel_ifjoin_switch(const char *caller
,
241 struct pim_ifchannel
*ch
,
242 enum pim_ifjoin_state new_state
)
244 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
246 if (PIM_DEBUG_PIM_EVENTS
)
247 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
250 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
, ch
->flags
),
251 pim_ifchannel_ifjoin_name (new_state
, 0));
254 if (old_state
== new_state
) {
255 if (PIM_DEBUG_PIM_EVENTS
) {
256 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
257 __PRETTY_FUNCTION__
, caller
, new_state
,
258 pim_ifchannel_ifjoin_name(new_state
, 0));
263 ch
->ifjoin_state
= new_state
;
265 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
267 struct pim_upstream
*up
= ch
->upstream
;
268 struct pim_upstream
*child
;
269 struct listnode
*up_node
;
273 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
)
275 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
277 struct channel_oil
*c_oil
= child
->channel_oil
;
278 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
280 if (PIM_DEBUG_PIM_TRACE
)
281 zlog_debug("%s %s: Prune(S,G)=%s from %s",
282 __FILE__
, __PRETTY_FUNCTION__
,
283 child
->sg_str
, up
->sg_str
);
287 if (!pim_upstream_evaluate_join_desired (child
))
289 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
290 pim_upstream_update_join_desired (child
);
294 * If the S,G has no if channel and the c_oil still
295 * has output here then the *,G was supplying the implied
296 * if channel. So remove it.
297 * I think this is dead code now. is it?
299 if (!ch
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
300 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
303 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
305 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
307 if (PIM_DEBUG_PIM_TRACE
)
308 zlog_debug("%s %s: Join(S,G)=%s from %s",
309 __FILE__
, __PRETTY_FUNCTION__
,
310 child
->sg_str
, up
->sg_str
);
312 if (pim_upstream_evaluate_join_desired (child
))
314 pim_channel_add_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
315 pim_upstream_update_join_desired (child
);
321 /* Transition to/from NOINFO ? */
322 if ((old_state
== PIM_IFJOIN_NOINFO
) ||
323 (new_state
== PIM_IFJOIN_NOINFO
)) {
325 if (PIM_DEBUG_PIM_EVENTS
) {
326 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
327 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN" : "UP"),
328 ch
->sg_str
, ch
->interface
->name
);
332 Record uptime of state transition to/from NOINFO
334 ch
->ifjoin_creation
= pim_time_monotonic_sec();
336 pim_upstream_update_join_desired(ch
->upstream
);
337 pim_ifchannel_update_could_assert(ch
);
338 pim_ifchannel_update_assert_tracking_desired(ch
);
342 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
345 switch (ifjoin_state
) {
346 case PIM_IFJOIN_NOINFO
:
347 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
352 case PIM_IFJOIN_JOIN
:
355 case PIM_IFJOIN_PRUNE
:
358 case PIM_IFJOIN_PRUNE_PENDING
:
361 case PIM_IFJOIN_PRUNE_TMP
:
364 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
369 return "ifjoin_bad_state";
372 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
374 switch (ifassert_state
) {
375 case PIM_IFASSERT_NOINFO
: return "NOINFO";
376 case PIM_IFASSERT_I_AM_WINNER
: return "WINNER";
377 case PIM_IFASSERT_I_AM_LOSER
: return "LOSER";
380 return "ifassert_bad_state";
384 RFC 4601: 4.6.5. Assert State Macros
386 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
387 defaults to Infinity when in the NoInfo state.
389 void reset_ifassert_state(struct pim_ifchannel
*ch
)
391 THREAD_OFF(ch
->t_ifassert_timer
);
393 pim_ifassert_winner_set(ch
,
396 qpim_infinite_assert_metric
);
399 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
400 struct prefix_sg
*sg
)
402 struct pim_interface
*pim_ifp
;
403 struct pim_ifchannel
*ch
;
404 struct pim_ifchannel lookup
;
409 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
411 pim_str_sg_dump (sg
),
417 ch
= hash_lookup (pim_ifp
->pim_ifchannel_hash
, &lookup
);
422 static void ifmembership_set(struct pim_ifchannel
*ch
,
423 enum pim_ifmembership membership
)
425 if (ch
->local_ifmembership
== membership
)
428 if (PIM_DEBUG_PIM_EVENTS
) {
429 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
432 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE" : "NOINFO",
433 ch
->interface
->name
);
436 ch
->local_ifmembership
= membership
;
438 pim_upstream_update_join_desired(ch
->upstream
);
439 pim_ifchannel_update_could_assert(ch
);
440 pim_ifchannel_update_assert_tracking_desired(ch
);
444 void pim_ifchannel_membership_clear(struct interface
*ifp
)
446 struct pim_interface
*pim_ifp
;
447 struct listnode
*ch_node
;
448 struct pim_ifchannel
*ch
;
453 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
454 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
458 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
460 struct pim_interface
*pim_ifp
;
461 struct listnode
*node
;
462 struct listnode
*next_node
;
463 struct pim_ifchannel
*ch
;
468 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
, ch
)) {
469 delete_on_noinfo(ch
);
474 * For a given Interface, if we are given a S,G
475 * Find the *,G (If we have it).
476 * If we are passed a *,G, find the *,* ifchannel
479 static struct pim_ifchannel
*
480 pim_ifchannel_find_parent (struct pim_ifchannel
*ch
)
482 struct prefix_sg parent_sg
= ch
->sg
;
483 struct pim_ifchannel
*parent
= NULL
;
486 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
) &&
487 (parent_sg
.grp
.s_addr
!= INADDR_ANY
))
489 parent_sg
.src
.s_addr
= INADDR_ANY
;
490 parent
= pim_ifchannel_find (ch
->interface
, &parent_sg
);
493 listnode_add (parent
->sources
, ch
);
500 struct pim_ifchannel
*
501 pim_ifchannel_add(struct interface
*ifp
,
502 struct prefix_sg
*sg
, int flags
)
504 struct pim_interface
*pim_ifp
;
505 struct pim_ifchannel
*ch
;
506 struct pim_upstream
*up
;
508 ch
= pim_ifchannel_find(ifp
, sg
);
514 up
= pim_upstream_add(sg
, NULL
, flags
, __PRETTY_FUNCTION__
);
516 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
518 pim_str_sg_dump (sg
), ifp
->name
);
522 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
524 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
526 up
->sg_str
, ifp
->name
);
528 pim_upstream_del (up
, __PRETTY_FUNCTION__
);
536 pim_str_sg_set (sg
, ch
->sg_str
);
537 ch
->parent
= pim_ifchannel_find_parent (ch
);
538 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
540 ch
->sources
= list_new ();
541 ch
->sources
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
546 pim_ifchannel_find_new_children (ch
);
547 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
549 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
550 ch
->t_ifjoin_expiry_timer
= NULL
;
551 ch
->t_ifjoin_prune_pending_timer
= NULL
;
552 ch
->ifjoin_creation
= 0;
554 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
555 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval (ch
);
557 ch
->ifassert_winner
.s_addr
= 0;
560 ch
->t_ifassert_timer
= NULL
;
561 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
562 reset_ifassert_state(ch
);
563 if (pim_macro_ch_could_assert_eval(ch
))
564 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
566 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
568 if (pim_macro_assert_tracking_desired_eval(ch
))
569 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
571 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
574 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
575 ch
= hash_get (pim_ifp
->pim_ifchannel_hash
, ch
, hash_alloc_intern
);
576 listnode_add_sort(pim_ifchannel_list
, ch
);
578 listnode_add_sort(up
->ifchannels
, ch
);
580 if (PIM_DEBUG_PIM_TRACE
)
581 zlog_debug ("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
, ch
->sg_str
);
586 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
588 pim_forward_stop(ch
);
589 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
591 delete_on_noinfo(ch
);
594 static int on_ifjoin_expiry_timer(struct thread
*t
)
596 struct pim_ifchannel
*ch
;
600 ch
->t_ifjoin_expiry_timer
= NULL
;
602 ifjoin_to_noinfo(ch
, true);
603 /* ch may have been deleted */
608 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
610 struct pim_ifchannel
*ch
;
611 int send_prune_echo
; /* boolean */
612 struct interface
*ifp
;
613 struct pim_interface
*pim_ifp
;
617 ch
->t_ifjoin_prune_pending_timer
= NULL
;
619 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
)
621 /* Send PruneEcho(S,G) ? */
624 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
630 rpf
.source_nexthop
.interface
= ifp
;
631 rpf
.rpf_addr
.u
.prefix4
= pim_ifp
->primary_address
;
632 pim_jp_agg_single_upstream_send(&rpf
, ch
->upstream
, 0);
634 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
635 message on RP path upon prune timer expiry.
637 if (PIM_IF_FLAG_TEST_S_G_RPT (ch
->flags
))
640 pim_upstream_update_join_desired(ch
->upstream
);
642 ch->ifjoin_state transition to NOINFO state
643 ch_del is set to 0 for not deleteing from here.
644 Holdtime expiry (ch_del set to 1) delete the entry.
646 ifjoin_to_noinfo(ch
, false);
649 ifjoin_to_noinfo(ch
, true);
650 /* from here ch may have been deleted */
654 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
655 __PRETTY_FUNCTION__
, pim_str_sg_dump (&ch
->sg
),
656 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
, ch
->flags
));
662 static void check_recv_upstream(int is_join
,
663 struct interface
*recv_ifp
,
664 struct in_addr upstream
,
665 struct prefix_sg
*sg
,
666 uint8_t source_flags
,
669 struct pim_upstream
*up
;
671 /* Upstream (S,G) in Joined state ? */
672 up
= pim_upstream_find(sg
);
675 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
678 /* Upstream (S,G) in Joined state */
680 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
681 /* RPF'(S,G) not found */
682 zlog_warn("%s %s: RPF'%s not found",
683 __FILE__
, __PRETTY_FUNCTION__
,
688 /* upstream directed to RPF'(S,G) ? */
689 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
690 char up_str
[INET_ADDRSTRLEN
];
691 char rpf_str
[PREFIX_STRLEN
];
692 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
693 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
, sizeof(rpf_str
));
694 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
695 __FILE__
, __PRETTY_FUNCTION__
,
697 up_str
, rpf_str
, recv_ifp
->name
);
700 /* upstream directed to RPF'(S,G) */
703 /* Join(S,G) to RPF'(S,G) */
704 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
, holdtime
);
708 /* Prune to RPF'(S,G) */
710 if (source_flags
& PIM_RPT_BIT_MASK
) {
711 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
712 /* Prune(*,G) to RPF'(S,G) */
713 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", up
);
717 /* Prune(S,G,rpt) to RPF'(S,G) */
718 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", up
);
722 /* Prune(S,G) to RPF'(S,G) */
723 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
727 nonlocal_upstream(int is_join
,
728 struct interface
*recv_ifp
,
729 struct in_addr upstream
,
730 struct prefix_sg
*sg
,
731 uint8_t source_flags
,
734 struct pim_interface
*recv_pim_ifp
;
735 int is_local
; /* boolean */
737 recv_pim_ifp
= recv_ifp
->info
;
738 zassert(recv_pim_ifp
);
740 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
745 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
746 char up_str
[INET_ADDRSTRLEN
];
747 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
748 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
750 is_join
? "join" : "prune",
751 pim_str_sg_dump (sg
),
752 up_str
, recv_ifp
->name
);
756 * Since recv upstream addr was not directed to our primary
757 * address, check if we should react to it in any way.
759 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
,
760 source_flags
, holdtime
);
762 return 1; /* non-local */
765 void pim_ifchannel_join_add(struct interface
*ifp
,
766 struct in_addr neigh_addr
,
767 struct in_addr upstream
,
768 struct prefix_sg
*sg
,
769 uint8_t source_flags
,
772 struct pim_interface
*pim_ifp
;
773 struct pim_ifchannel
*ch
;
775 if (nonlocal_upstream(1 /* join */, ifp
, upstream
,
776 sg
, source_flags
, holdtime
)) {
780 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
785 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
787 Transitions from "I am Assert Loser" State
789 Receive Join(S,G) on Interface I
791 We receive a Join(S,G) that has the Upstream Neighbor Address
792 field set to my primary IP address on interface I. The action is
793 to transition to NoInfo state, delete this (S,G) assert state
794 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
797 Notice: The nonlocal_upstream() test above ensures the upstream
798 address of the join message is our primary address.
800 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
801 char neigh_str
[INET_ADDRSTRLEN
];
802 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
, sizeof(neigh_str
));
803 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
805 ch
->sg_str
, neigh_str
, ifp
->name
);
807 assert_action_a5(ch
);
813 switch (ch
->ifjoin_state
) {
814 case PIM_IFJOIN_NOINFO
:
815 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
816 if (pim_macro_chisin_oiflist(ch
)) {
817 pim_upstream_inherited_olist (ch
->upstream
);
818 pim_forward_start(ch
);
821 * If we are going to be a LHR, we need to note it
823 if (ch
->upstream
->parent
&&
824 (ch
->upstream
->parent
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
) &&
825 !(ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_LHR
))
827 pim_upstream_ref (ch
->upstream
, PIM_UPSTREAM_FLAG_MASK_SRC_LHR
, __PRETTY_FUNCTION__
);
828 pim_upstream_keep_alive_timer_start (ch
->upstream
, qpim_keep_alive_time
);
831 case PIM_IFJOIN_JOIN
:
832 zassert(!ch
->t_ifjoin_prune_pending_timer
);
835 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
836 previously received join message with holdtime=0xFFFF.
838 if (ch
->t_ifjoin_expiry_timer
) {
839 unsigned long remain
=
840 thread_timer_remain_second(ch
->t_ifjoin_expiry_timer
);
841 if (remain
> holdtime
) {
843 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
845 Transitions from Join State
847 The (S,G) downstream state machine on interface I remains in
848 Join state, and the Expiry Timer (ET) is restarted, set to
849 maximum of its current value and the HoldTime from the
850 triggering Join/Prune message.
852 Conclusion: Do not change the ET if the current value is
853 higher than the received join holdtime.
858 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
860 case PIM_IFJOIN_PRUNE
:
861 if (source_flags
& PIM_ENCODE_RPT_BIT
)
862 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
864 case PIM_IFJOIN_PRUNE_PENDING
:
865 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
866 if (source_flags
& PIM_ENCODE_RPT_BIT
)
868 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
869 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
872 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
874 case PIM_IFJOIN_PRUNE_TMP
:
876 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
880 if (holdtime
!= 0xFFFF) {
881 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
882 on_ifjoin_expiry_timer
,
887 void pim_ifchannel_prune(struct interface
*ifp
,
888 struct in_addr upstream
,
889 struct prefix_sg
*sg
,
890 uint8_t source_flags
,
893 struct pim_ifchannel
*ch
;
894 struct pim_interface
*pim_ifp
;
895 int jp_override_interval_msec
;
897 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
,
898 sg
, source_flags
, holdtime
)) {
902 ch
= pim_ifchannel_find (ifp
, sg
);
903 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
))
906 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
907 __PRETTY_FUNCTION__
, ifp
->name
, pim_str_sg_dump (sg
), source_flags
);
911 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
917 switch (ch
->ifjoin_state
) {
918 case PIM_IFJOIN_NOINFO
:
919 if (source_flags
& PIM_ENCODE_RPT_BIT
)
921 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
922 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
924 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
925 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
926 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
928 jp_override_interval_msec
= 0; /* schedule to expire immediately */
929 /* If we called ifjoin_prune() directly instead, care should
930 be taken not to use "ch" afterwards since it would be
933 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
934 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
935 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
936 on_ifjoin_prune_pending_timer
,
937 ch
, jp_override_interval_msec
);
938 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
939 on_ifjoin_expiry_timer
,
941 pim_upstream_update_join_desired(ch
->upstream
);
944 case PIM_IFJOIN_PRUNE_PENDING
:
947 case PIM_IFJOIN_JOIN
:
948 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
950 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_PRUNE_PENDING
);
952 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
953 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
955 jp_override_interval_msec
= 0; /* schedule to expire immediately */
956 /* If we called ifjoin_prune() directly instead, care should
957 be taken not to use "ch" afterwards since it would be
959 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
960 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
961 on_ifjoin_prune_pending_timer
,
962 ch
, jp_override_interval_msec
);
964 case PIM_IFJOIN_PRUNE
:
965 if (source_flags
& PIM_ENCODE_RPT_BIT
)
967 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
968 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
969 on_ifjoin_expiry_timer
,
973 case PIM_IFJOIN_PRUNE_TMP
:
974 if (source_flags
& PIM_ENCODE_RPT_BIT
)
976 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
977 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
978 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
979 on_ifjoin_expiry_timer
,
983 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
984 if (source_flags
& PIM_ENCODE_RPT_BIT
)
986 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
987 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
988 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
989 on_ifjoin_expiry_timer
,
997 pim_ifchannel_local_membership_add(struct interface
*ifp
,
998 struct prefix_sg
*sg
)
1000 struct pim_ifchannel
*ch
, *starch
;
1001 struct pim_interface
*pim_ifp
;
1003 /* PIM enabled on interface? */
1004 pim_ifp
= ifp
->info
;
1007 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1010 /* skip (*,G) ch creation if G is of type SSM */
1011 if (sg
->src
.s_addr
== INADDR_ANY
)
1013 if (pim_is_grp_ssm (sg
->grp
))
1015 if (PIM_DEBUG_PIM_EVENTS
)
1016 zlog_debug("%s: local membership (S,G)=%s ignored as group is SSM",
1017 __PRETTY_FUNCTION__
, pim_str_sg_dump (sg
));
1022 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1027 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1029 if (sg
->src
.s_addr
== INADDR_ANY
)
1031 struct pim_upstream
*up
= pim_upstream_find (sg
);
1032 struct pim_upstream
*child
;
1033 struct listnode
*up_node
;
1037 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
1039 if (PIM_DEBUG_EVENTS
)
1040 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1041 __FILE__
, __PRETTY_FUNCTION__
,
1042 child
->sg_str
, ifp
->name
, up
->sg_str
);
1044 ch
= pim_ifchannel_find (ifp
, &child
->sg
);
1045 if (pim_upstream_evaluate_join_desired_interface (child
, ch
, starch
))
1047 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1048 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
1052 if (pimg
->spt
.switchover
== PIM_SPT_INFINITY
)
1054 if (pimg
->spt
.plist
)
1056 struct prefix_list
*plist
= prefix_list_lookup (AFI_IP
, pimg
->spt
.plist
);
1059 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1060 g
.u
.prefix4
= up
->sg
.grp
;
1062 if (prefix_list_apply (plist
, &g
) == PREFIX_DENY
)
1064 pim_channel_add_oif (up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_IGMP
);
1069 pim_channel_add_oif (up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_IGMP
);
1075 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1076 struct prefix_sg
*sg
)
1078 struct pim_ifchannel
*starch
, *ch
, *orig
;
1079 struct pim_interface
*pim_ifp
;
1081 /* PIM enabled on interface? */
1082 pim_ifp
= ifp
->info
;
1085 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1088 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1092 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1094 if (sg
->src
.s_addr
== INADDR_ANY
)
1096 struct pim_upstream
*up
= pim_upstream_find (sg
);
1097 struct pim_upstream
*child
;
1098 struct listnode
*up_node
, *up_nnode
;
1102 for (ALL_LIST_ELEMENTS (up
->sources
, up_node
, up_nnode
, child
))
1104 struct channel_oil
*c_oil
= child
->channel_oil
;
1105 struct pim_ifchannel
*chchannel
= pim_ifchannel_find (ifp
, &child
->sg
);
1106 struct pim_interface
*pim_ifp
= ifp
->info
;
1108 if (PIM_DEBUG_EVENTS
)
1109 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1110 __FILE__
, __PRETTY_FUNCTION__
,
1111 up
->sg_str
, ifp
->name
, child
->sg_str
);
1113 ch
= pim_ifchannel_find (ifp
, &child
->sg
);
1114 if (c_oil
&& !pim_upstream_evaluate_join_desired_interface (child
, ch
, starch
))
1115 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1118 * If the S,G has no if channel and the c_oil still
1119 * has output here then the *,G was supplying the implied
1120 * if channel. So remove it.
1122 if (!chchannel
&& c_oil
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1123 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1125 /* Child node removal/ref count-- will happen as part of parent' delete_no_info */
1128 delete_on_noinfo(orig
);
1131 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1133 int old_couldassert
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1134 int new_couldassert
= PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1136 if (new_couldassert
== old_couldassert
)
1139 if (PIM_DEBUG_PIM_EVENTS
) {
1140 char src_str
[INET_ADDRSTRLEN
];
1141 char grp_str
[INET_ADDRSTRLEN
];
1142 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1143 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1144 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1145 __PRETTY_FUNCTION__
,
1146 src_str
, grp_str
, ch
->interface
->name
,
1147 old_couldassert
, new_couldassert
);
1150 if (new_couldassert
) {
1151 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1152 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1155 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1156 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1158 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1159 assert_action_a4(ch
);
1163 pim_ifchannel_update_my_assert_metric(ch
);
1167 my_assert_metric may be affected by:
1170 pim_ifp->primary_address
1171 rpf->source_nexthop.mrib_metric_preference;
1172 rpf->source_nexthop.mrib_route_metric;
1174 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1176 struct pim_assert_metric my_metric_new
= pim_macro_ch_my_assert_metric_eval(ch
);
1178 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1181 if (PIM_DEBUG_PIM_EVENTS
) {
1182 char src_str
[INET_ADDRSTRLEN
];
1183 char grp_str
[INET_ADDRSTRLEN
];
1184 char old_addr_str
[INET_ADDRSTRLEN
];
1185 char new_addr_str
[INET_ADDRSTRLEN
];
1186 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1187 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1188 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
, old_addr_str
, sizeof(old_addr_str
));
1189 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
, new_addr_str
, sizeof(new_addr_str
));
1190 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1191 __PRETTY_FUNCTION__
,
1192 src_str
, grp_str
, ch
->interface
->name
,
1193 ch
->ifassert_my_metric
.rpt_bit_flag
,
1194 ch
->ifassert_my_metric
.metric_preference
,
1195 ch
->ifassert_my_metric
.route_metric
,
1197 my_metric_new
.rpt_bit_flag
,
1198 my_metric_new
.metric_preference
,
1199 my_metric_new
.route_metric
,
1203 ch
->ifassert_my_metric
= my_metric_new
;
1205 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1206 &ch
->ifassert_winner_metric
)) {
1207 assert_action_a5(ch
);
1211 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1213 int old_atd
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1214 int new_atd
= PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1216 if (new_atd
== old_atd
)
1219 if (PIM_DEBUG_PIM_EVENTS
) {
1220 char src_str
[INET_ADDRSTRLEN
];
1221 char grp_str
[INET_ADDRSTRLEN
];
1222 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1223 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1224 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1225 __PRETTY_FUNCTION__
,
1226 src_str
, grp_str
, ch
->interface
->name
,
1231 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1232 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1235 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1236 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1238 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1239 assert_action_a5(ch
);
1245 * If we have a new pim interface, check to
1246 * see if any of the pre-existing channels have
1247 * their upstream out that way and turn on forwarding
1248 * for that ifchannel then.
1251 pim_ifchannel_scan_forward_start (struct interface
*new_ifp
)
1253 struct listnode
*ifnode
;
1254 struct interface
*ifp
;
1255 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1257 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), ifnode
, ifp
))
1259 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1260 struct listnode
*ch_node
;
1261 struct pim_ifchannel
*ch
;
1266 if (new_pim_ifp
== loop_pim_ifp
)
1269 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp
->pim_ifchannel_list
, ch_node
, ch
))
1271 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
1273 struct pim_upstream
*up
= ch
->upstream
;
1274 if ((!up
->channel_oil
) &&
1275 (up
->rpf
.source_nexthop
.interface
== new_ifp
))
1276 pim_forward_start (ch
);
1283 * Downstream per-interface (S,G,rpt) state machine
1284 * states that we need to move (S,G,rpt) items
1285 * into different states at the start of the
1286 * reception of a *,G join as well, when
1287 * we get End of Message
1290 pim_ifchannel_set_star_g_join_state (struct pim_ifchannel
*ch
, int eom
, uint8_t source_flags
, uint8_t join
, uint8_t starg_alone
)
1292 struct pim_ifchannel
*child
;
1293 struct listnode
*ch_node
;
1295 if (PIM_DEBUG_PIM_TRACE
)
1296 zlog_debug ("%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1297 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1298 ch
->sg_str
, eom
, join
);
1302 for (ALL_LIST_ELEMENTS_RO (ch
->sources
, ch_node
, child
))
1304 /* Only *,G Join received and no (SG-RPT) prune.
1305 eom = 1, only (W,G) join_alone is true, WC and RPT are set.
1306 Scan all S,G associated to G and if any SG-RPT
1307 remove the SG-RPT flag.
1309 if (eom
&& starg_alone
&& (source_flags
& PIM_RPT_BIT_MASK
) &&
1310 (source_flags
& PIM_WILDCARD_BIT_MASK
))
1312 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1314 struct pim_upstream
*up
= child
->upstream
;
1316 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1319 if (PIM_DEBUG_TRACE
)
1320 zlog_debug ("%s: SGRpt flag is cleared, add inherit oif to up %s",
1321 __PRETTY_FUNCTION__
, up
->sg_str
);
1322 pim_channel_add_oif (up
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1323 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, child
, PIM_IFJOIN_JOIN
);
1328 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1331 switch (child
->ifjoin_state
)
1333 case PIM_IFJOIN_NOINFO
:
1334 case PIM_IFJOIN_JOIN
:
1336 case PIM_IFJOIN_PRUNE
:
1338 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1340 case PIM_IFJOIN_PRUNE_PENDING
:
1342 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING_TMP
;
1344 case PIM_IFJOIN_PRUNE_TMP
:
1345 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1347 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1354 pim_ifchannel_hash_key (void *arg
)
1356 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1358 return jhash_2words (ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);
1362 pim_ifchannel_equal (const void *arg1
, const void *arg2
)
1364 const struct pim_ifchannel
*ch1
= (const struct pim_ifchannel
*)arg1
;
1365 const struct pim_ifchannel
*ch2
= (const struct pim_ifchannel
*)arg2
;
1367 if ((ch1
->sg
.grp
.s_addr
== ch2
->sg
.grp
.s_addr
) &&
1368 (ch1
->sg
.src
.s_addr
== ch2
->sg
.src
.s_addr
))