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,
32 #include "pim_iface.h"
33 #include "pim_ifchannel.h"
34 #include "pim_zebra.h"
40 #include "pim_macro.h"
42 #include "pim_upstream.h"
45 pim_ifchannel_compare (struct pim_ifchannel
*ch1
, struct pim_ifchannel
*ch2
)
47 struct pim_interface
*pim_ifp1
;
48 struct pim_interface
*pim_ifp2
;
50 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
53 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
56 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
59 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
62 pim_ifp1
= ch1
->interface
->info
;
63 pim_ifp2
= ch2
->interface
->info
;
64 if (ntohl(pim_ifp1
->primary_address
.s_addr
) < ntohl(pim_ifp2
->primary_address
.s_addr
))
67 if (ntohl(pim_ifp1
->primary_address
.s_addr
) > ntohl(pim_ifp2
->primary_address
.s_addr
))
70 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
73 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
80 * A (*,G) or a (*,*) is going away
81 * remove the parent pointer from
82 * those pointing at us
85 pim_ifchannel_remove_children (struct pim_ifchannel
*ch
)
87 struct pim_ifchannel
*child
;
92 while (!list_isempty (ch
->sources
))
94 child
= listnode_head (ch
->sources
);
96 listnode_delete (ch
->sources
, child
);
101 * A (*,G) or a (*,*) is being created
102 * find all the children that would point
106 pim_ifchannel_find_new_children (struct pim_ifchannel
*ch
)
108 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
109 struct pim_ifchannel
*child
;
110 struct listnode
*ch_node
;
112 // Basic Sanity that we are not being silly
113 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
114 (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
117 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
) &&
118 (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
121 for (ALL_LIST_ELEMENTS_RO (pim_ifp
->pim_ifchannel_list
, ch_node
, child
))
123 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
) &&
124 (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
) &&
128 listnode_add_sort (ch
->sources
, child
);
133 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
135 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
138 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
140 struct pim_interface
*pim_ifp
;
142 pim_ifp
= ch
->interface
->info
;
145 * When this channel is removed
146 * we need to find all our children
147 * and make sure our pointers are fixed
149 pim_ifchannel_remove_children (ch
);
152 list_delete (ch
->sources
);
154 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
155 pim_upstream_update_join_desired(ch
->upstream
);
158 pim_upstream_del(ch
->upstream
, __PRETTY_FUNCTION__
);
161 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
162 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
163 THREAD_OFF(ch
->t_ifassert_timer
);
167 listnode_delete (ch
->parent
->sources
, ch
);
171 notice that listnode_delete() can't be moved
172 into pim_ifchannel_free() because the later is
173 called by list_delete_all_node()
175 listnode_delete(pim_ifp
->pim_ifchannel_list
, ch
);
176 listnode_delete(pim_ifchannel_list
, ch
);
178 pim_ifchannel_free(ch
);
182 pim_ifchannel_delete_all (struct interface
*ifp
)
184 struct pim_interface
*pim_ifp
;
185 struct listnode
*ifchannel_node
;
186 struct listnode
*ifchannel_nextnode
;
187 struct pim_ifchannel
*ifchannel
;
191 for (ALL_LIST_ELEMENTS (pim_ifp
->pim_ifchannel_list
, ifchannel_node
,
192 ifchannel_nextnode
, ifchannel
))
194 pim_ifchannel_delete (ifchannel
);
198 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
200 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
&&
201 ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
&&
202 ch
->t_ifjoin_expiry_timer
== NULL
)
203 pim_ifchannel_delete(ch
);
207 void pim_ifchannel_ifjoin_switch(const char *caller
,
208 struct pim_ifchannel
*ch
,
209 enum pim_ifjoin_state new_state
)
211 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
213 if (PIM_DEBUG_PIM_EVENTS
)
214 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
217 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
),
218 pim_ifchannel_ifjoin_name (new_state
));
221 if (old_state
== new_state
) {
222 if (PIM_DEBUG_PIM_EVENTS
) {
223 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
224 __PRETTY_FUNCTION__
, caller
, new_state
,
225 pim_ifchannel_ifjoin_name(new_state
));
230 ch
->ifjoin_state
= new_state
;
232 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
234 struct pim_upstream
*up
= ch
->upstream
;
235 struct pim_upstream
*child
;
236 struct listnode
*up_node
;
240 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
)
242 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
244 struct channel_oil
*c_oil
= child
->channel_oil
;
245 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
247 if (PIM_DEBUG_PIM_TRACE
)
248 zlog_debug("%s %s: Prune(S,G)=%s from %s",
249 __FILE__
, __PRETTY_FUNCTION__
,
250 child
->sg_str
, up
->sg_str
);
254 if (!pim_upstream_evaluate_join_desired (child
))
255 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_PIM
);
258 * If the S,G has no if channel and the c_oil still
259 * has output here then the *,G was supplying the implied
260 * if channel. So remove it.
262 if (!ch
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
263 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_PIM
);
266 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
268 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
270 if (PIM_DEBUG_PIM_TRACE
)
271 zlog_debug("%s %s: Join(S,G)=%s from %s",
272 __FILE__
, __PRETTY_FUNCTION__
,
273 child
->sg_str
, up
->sg_str
);
275 if (pim_upstream_evaluate_join_desired (child
))
277 pim_channel_add_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_PIM
);
278 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
284 /* Transition to/from NOINFO ? */
285 if ((old_state
== PIM_IFJOIN_NOINFO
) ||
286 (new_state
== PIM_IFJOIN_NOINFO
)) {
288 if (PIM_DEBUG_PIM_EVENTS
) {
289 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
290 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN" : "UP"),
291 ch
->sg_str
, ch
->interface
->name
);
295 Record uptime of state transition to/from NOINFO
297 ch
->ifjoin_creation
= pim_time_monotonic_sec();
299 pim_upstream_update_join_desired(ch
->upstream
);
300 pim_ifchannel_update_could_assert(ch
);
301 pim_ifchannel_update_assert_tracking_desired(ch
);
305 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
)
307 switch (ifjoin_state
) {
308 case PIM_IFJOIN_NOINFO
: return "NOINFO";
309 case PIM_IFJOIN_JOIN
: return "JOIN";
310 case PIM_IFJOIN_PRUNE
: return "PRUNE";
311 case PIM_IFJOIN_PRUNE_PENDING
: return "PRUNEP";
312 case PIM_IFJOIN_PRUNE_TMP
: return "PRUNET";
313 case PIM_IFJOIN_PRUNE_PENDING_TMP
: return "PRUNEPT";
316 return "ifjoin_bad_state";
319 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
321 switch (ifassert_state
) {
322 case PIM_IFASSERT_NOINFO
: return "NOINFO";
323 case PIM_IFASSERT_I_AM_WINNER
: return "WINNER";
324 case PIM_IFASSERT_I_AM_LOSER
: return "LOSER";
327 return "ifassert_bad_state";
331 RFC 4601: 4.6.5. Assert State Macros
333 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
334 defaults to Infinity when in the NoInfo state.
336 void reset_ifassert_state(struct pim_ifchannel
*ch
)
338 THREAD_OFF(ch
->t_ifassert_timer
);
340 pim_ifassert_winner_set(ch
,
343 qpim_infinite_assert_metric
);
346 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
347 struct prefix_sg
*sg
)
349 struct pim_interface
*pim_ifp
;
350 struct listnode
*ch_node
;
351 struct pim_ifchannel
*ch
;
358 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
360 pim_str_sg_dump (sg
),
365 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
367 (sg
->src
.s_addr
== ch
->sg
.src
.s_addr
) &&
368 (sg
->grp
.s_addr
== ch
->sg
.grp
.s_addr
)
377 static void ifmembership_set(struct pim_ifchannel
*ch
,
378 enum pim_ifmembership membership
)
380 if (ch
->local_ifmembership
== membership
)
383 if (PIM_DEBUG_PIM_EVENTS
) {
384 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
387 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE" : "NOINFO",
388 ch
->interface
->name
);
391 ch
->local_ifmembership
= membership
;
393 pim_upstream_update_join_desired(ch
->upstream
);
394 pim_ifchannel_update_could_assert(ch
);
395 pim_ifchannel_update_assert_tracking_desired(ch
);
399 void pim_ifchannel_membership_clear(struct interface
*ifp
)
401 struct pim_interface
*pim_ifp
;
402 struct listnode
*ch_node
;
403 struct pim_ifchannel
*ch
;
408 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
409 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
413 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
415 struct pim_interface
*pim_ifp
;
416 struct listnode
*node
;
417 struct listnode
*next_node
;
418 struct pim_ifchannel
*ch
;
423 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
, ch
)) {
424 delete_on_noinfo(ch
);
429 * For a given Interface, if we are given a S,G
430 * Find the *,G (If we have it).
431 * If we are passed a *,G, find the *,* ifchannel
434 static struct pim_ifchannel
*
435 pim_ifchannel_find_parent (struct pim_ifchannel
*ch
)
437 struct prefix_sg parent_sg
= ch
->sg
;
438 struct pim_ifchannel
*parent
= NULL
;
441 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
) &&
442 (parent_sg
.grp
.s_addr
!= INADDR_ANY
))
444 parent_sg
.src
.s_addr
= INADDR_ANY
;
445 parent
= pim_ifchannel_find (ch
->interface
, &parent_sg
);
448 listnode_add (parent
->sources
, ch
);
455 struct pim_ifchannel
*
456 pim_ifchannel_add(struct interface
*ifp
,
457 struct prefix_sg
*sg
, int flags
)
459 struct pim_interface
*pim_ifp
;
460 struct pim_ifchannel
*ch
;
461 struct pim_upstream
*up
;
463 ch
= pim_ifchannel_find(ifp
, sg
);
469 up
= pim_upstream_add(sg
, NULL
, flags
, __PRETTY_FUNCTION__
);
471 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
473 pim_str_sg_dump (sg
), ifp
->name
);
477 ch
= XMALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
479 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
481 up
->sg_str
, ifp
->name
);
483 pim_upstream_del (up
, __PRETTY_FUNCTION__
);
491 pim_str_sg_set (sg
, ch
->sg_str
);
492 ch
->parent
= pim_ifchannel_find_parent (ch
);
493 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
495 ch
->sources
= list_new ();
496 ch
->sources
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
501 pim_ifchannel_find_new_children (ch
);
502 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
504 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
505 ch
->t_ifjoin_expiry_timer
= NULL
;
506 ch
->t_ifjoin_prune_pending_timer
= NULL
;
507 ch
->ifjoin_creation
= 0;
509 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
510 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval (ch
);
512 ch
->ifassert_winner
.s_addr
= 0;
515 ch
->t_ifassert_timer
= NULL
;
516 reset_ifassert_state(ch
);
517 if (pim_macro_ch_could_assert_eval(ch
))
518 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
520 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
522 if (pim_macro_assert_tracking_desired_eval(ch
))
523 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
525 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
528 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
529 listnode_add_sort(pim_ifchannel_list
, ch
);
534 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
536 pim_forward_stop(ch
);
537 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
538 delete_on_noinfo(ch
);
541 static int on_ifjoin_expiry_timer(struct thread
*t
)
543 struct pim_ifchannel
*ch
;
547 ch
->t_ifjoin_expiry_timer
= NULL
;
549 ifjoin_to_noinfo(ch
);
550 /* ch may have been deleted */
555 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
557 struct pim_ifchannel
*ch
;
558 int send_prune_echo
; /* boolean */
559 struct interface
*ifp
;
560 struct pim_interface
*pim_ifp
;
564 ch
->t_ifjoin_prune_pending_timer
= NULL
;
566 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
)
568 /* Send PruneEcho(S,G) ? */
571 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
573 ifjoin_to_noinfo(ch
);
574 /* from here ch may have been deleted */
577 pim_joinprune_send (ifp
, pim_ifp
->primary_address
,
582 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
583 __PRETTY_FUNCTION__
, pim_str_sg_dump (&ch
->sg
),
584 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
));
590 static void check_recv_upstream(int is_join
,
591 struct interface
*recv_ifp
,
592 struct in_addr upstream
,
593 struct prefix_sg
*sg
,
594 uint8_t source_flags
,
597 struct pim_upstream
*up
;
599 /* Upstream (S,G) in Joined state ? */
600 up
= pim_upstream_find(sg
);
603 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
606 /* Upstream (S,G) in Joined state */
608 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
609 /* RPF'(S,G) not found */
610 zlog_warn("%s %s: RPF'%s not found",
611 __FILE__
, __PRETTY_FUNCTION__
,
616 /* upstream directed to RPF'(S,G) ? */
617 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
618 char up_str
[INET_ADDRSTRLEN
];
619 char rpf_str
[PREFIX_STRLEN
];
620 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
621 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
, sizeof(rpf_str
));
622 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
623 __FILE__
, __PRETTY_FUNCTION__
,
625 up_str
, rpf_str
, recv_ifp
->name
);
628 /* upstream directed to RPF'(S,G) */
631 /* Join(S,G) to RPF'(S,G) */
632 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
, holdtime
);
636 /* Prune to RPF'(S,G) */
638 if (source_flags
& PIM_RPT_BIT_MASK
) {
639 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
640 /* Prune(*,G) to RPF'(S,G) */
641 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
642 up
, up
->rpf
.rpf_addr
.u
.prefix4
);
646 /* Prune(S,G,rpt) to RPF'(S,G) */
647 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
648 up
, up
->rpf
.rpf_addr
.u
.prefix4
);
652 /* Prune(S,G) to RPF'(S,G) */
653 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
,
654 up
->rpf
.rpf_addr
.u
.prefix4
);
657 static int nonlocal_upstream(int is_join
,
658 struct interface
*recv_ifp
,
659 struct in_addr upstream
,
660 struct prefix_sg
*sg
,
661 uint8_t source_flags
,
664 struct pim_interface
*recv_pim_ifp
;
665 int is_local
; /* boolean */
667 recv_pim_ifp
= recv_ifp
->info
;
668 zassert(recv_pim_ifp
);
670 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
672 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
673 char up_str
[INET_ADDRSTRLEN
];
674 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
675 zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s",
677 is_join
? "join" : "prune",
678 pim_str_sg_dump (sg
),
679 is_local
? "local" : "non-local",
680 up_str
, recv_ifp
->name
);
687 Since recv upstream addr was not directed to our primary
688 address, check if we should react to it in any way.
690 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
,
691 source_flags
, holdtime
);
693 return 1; /* non-local */
696 void pim_ifchannel_join_add(struct interface
*ifp
,
697 struct in_addr neigh_addr
,
698 struct in_addr upstream
,
699 struct prefix_sg
*sg
,
700 uint8_t source_flags
,
703 struct pim_interface
*pim_ifp
;
704 struct pim_ifchannel
*ch
;
706 if (nonlocal_upstream(1 /* join */, ifp
, upstream
,
707 sg
, source_flags
, holdtime
)) {
711 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
716 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
718 Transitions from "I am Assert Loser" State
720 Receive Join(S,G) on Interface I
722 We receive a Join(S,G) that has the Upstream Neighbor Address
723 field set to my primary IP address on interface I. The action is
724 to transition to NoInfo state, delete this (S,G) assert state
725 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
728 Notice: The nonlocal_upstream() test above ensures the upstream
729 address of the join message is our primary address.
731 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
732 char neigh_str
[INET_ADDRSTRLEN
];
733 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
, sizeof(neigh_str
));
734 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
736 ch
->sg_str
, neigh_str
, ifp
->name
);
738 assert_action_a5(ch
);
744 switch (ch
->ifjoin_state
) {
745 case PIM_IFJOIN_NOINFO
:
746 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
747 if (pim_macro_chisin_oiflist(ch
)) {
748 pim_upstream_inherited_olist (ch
->upstream
);
749 pim_forward_start(ch
);
752 case PIM_IFJOIN_JOIN
:
753 zassert(!ch
->t_ifjoin_prune_pending_timer
);
756 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
757 previously received join message with holdtime=0xFFFF.
759 if (ch
->t_ifjoin_expiry_timer
) {
760 unsigned long remain
=
761 thread_timer_remain_second(ch
->t_ifjoin_expiry_timer
);
762 if (remain
> holdtime
) {
764 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
766 Transitions from Join State
768 The (S,G) downstream state machine on interface I remains in
769 Join state, and the Expiry Timer (ET) is restarted, set to
770 maximum of its current value and the HoldTime from the
771 triggering Join/Prune message.
773 Conclusion: Do not change the ET if the current value is
774 higher than the received join holdtime.
779 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
781 case PIM_IFJOIN_PRUNE
:
782 if (source_flags
& PIM_ENCODE_RPT_BIT
)
783 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
785 case PIM_IFJOIN_PRUNE_PENDING
:
786 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
787 if (source_flags
& PIM_ENCODE_RPT_BIT
)
789 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
790 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
793 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
795 case PIM_IFJOIN_PRUNE_TMP
:
797 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
801 if (holdtime
!= 0xFFFF) {
802 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
803 on_ifjoin_expiry_timer
,
808 void pim_ifchannel_prune(struct interface
*ifp
,
809 struct in_addr upstream
,
810 struct prefix_sg
*sg
,
811 uint8_t source_flags
,
814 struct pim_ifchannel
*ch
;
815 struct pim_interface
*pim_ifp
;
816 int jp_override_interval_msec
;
818 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
,
819 sg
, source_flags
, holdtime
)) {
823 ch
= pim_ifchannel_find (ifp
, sg
);
824 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
))
827 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
828 __PRETTY_FUNCTION__
, ifp
->name
, pim_str_sg_dump (sg
), source_flags
);
832 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
838 switch (ch
->ifjoin_state
) {
839 case PIM_IFJOIN_NOINFO
:
840 if (source_flags
& PIM_ENCODE_RPT_BIT
)
842 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
843 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
844 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
845 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
847 jp_override_interval_msec
= 0; /* schedule to expire immediately */
848 /* If we called ifjoin_prune() directly instead, care should
849 be taken not to use "ch" afterwards since it would be
852 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
853 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
854 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
855 on_ifjoin_prune_pending_timer
,
856 ch
, jp_override_interval_msec
);
857 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
858 on_ifjoin_expiry_timer
,
862 case PIM_IFJOIN_PRUNE_PENDING
:
865 case PIM_IFJOIN_JOIN
:
866 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
868 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_PRUNE_PENDING
);
870 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
871 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
873 jp_override_interval_msec
= 0; /* schedule to expire immediately */
874 /* If we called ifjoin_prune() directly instead, care should
875 be taken not to use "ch" afterwards since it would be
877 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
878 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
879 on_ifjoin_prune_pending_timer
,
880 ch
, jp_override_interval_msec
);
882 case PIM_IFJOIN_PRUNE
:
883 if (source_flags
& PIM_ENCODE_RPT_BIT
)
885 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
886 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
887 on_ifjoin_expiry_timer
,
891 case PIM_IFJOIN_PRUNE_TMP
:
892 if (source_flags
& PIM_ENCODE_RPT_BIT
)
894 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
895 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
896 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
897 on_ifjoin_expiry_timer
,
901 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
902 if (source_flags
& PIM_ENCODE_RPT_BIT
)
904 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
905 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
906 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
907 on_ifjoin_expiry_timer
,
914 void pim_ifchannel_local_membership_add(struct interface
*ifp
,
915 struct prefix_sg
*sg
)
917 struct pim_ifchannel
*ch
;
918 struct pim_interface
*pim_ifp
;
920 /* PIM enabled on interface? */
924 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
927 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
932 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
934 if (sg
->src
.s_addr
== INADDR_ANY
)
936 struct pim_upstream
*up
= pim_upstream_find (sg
);
937 struct pim_upstream
*child
;
938 struct listnode
*up_node
;
940 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
942 if (PIM_DEBUG_EVENTS
)
943 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
944 __FILE__
, __PRETTY_FUNCTION__
,
945 child
->sg_str
, ifp
->name
, up
->sg_str
);
947 if (pim_upstream_evaluate_join_desired (child
))
949 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
950 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
956 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
957 struct prefix_sg
*sg
)
959 struct pim_ifchannel
*ch
;
960 struct pim_interface
*pim_ifp
;
962 /* PIM enabled on interface? */
966 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
969 ch
= pim_ifchannel_find(ifp
, sg
);
973 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
975 if (sg
->src
.s_addr
== INADDR_ANY
)
977 struct pim_upstream
*up
= pim_upstream_find (sg
);
978 struct pim_upstream
*child
;
979 struct listnode
*up_node
;
981 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
983 struct channel_oil
*c_oil
= child
->channel_oil
;
984 struct pim_ifchannel
*chchannel
= pim_ifchannel_find (ifp
, &child
->sg
);
985 struct pim_interface
*pim_ifp
= ifp
->info
;
987 if (PIM_DEBUG_EVENTS
)
988 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
989 __FILE__
, __PRETTY_FUNCTION__
,
990 up
->sg_str
, ifp
->name
, child
->sg_str
);
992 if (c_oil
&& !pim_upstream_evaluate_join_desired (child
))
993 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
996 * If the S,G has no if channel and the c_oil still
997 * has output here then the *,G was supplying the implied
998 * if channel. So remove it.
1000 if (!chchannel
&& c_oil
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1001 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
1004 delete_on_noinfo(ch
);
1007 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1009 int old_couldassert
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1010 int new_couldassert
= PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1012 if (new_couldassert
== old_couldassert
)
1015 if (PIM_DEBUG_PIM_EVENTS
) {
1016 char src_str
[INET_ADDRSTRLEN
];
1017 char grp_str
[INET_ADDRSTRLEN
];
1018 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1019 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1020 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1021 __PRETTY_FUNCTION__
,
1022 src_str
, grp_str
, ch
->interface
->name
,
1023 old_couldassert
, new_couldassert
);
1026 if (new_couldassert
) {
1027 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1028 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1031 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1032 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1034 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1035 assert_action_a4(ch
);
1039 pim_ifchannel_update_my_assert_metric(ch
);
1043 my_assert_metric may be affected by:
1046 pim_ifp->primary_address
1047 rpf->source_nexthop.mrib_metric_preference;
1048 rpf->source_nexthop.mrib_route_metric;
1050 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1052 struct pim_assert_metric my_metric_new
= pim_macro_ch_my_assert_metric_eval(ch
);
1054 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1057 if (PIM_DEBUG_PIM_EVENTS
) {
1058 char src_str
[INET_ADDRSTRLEN
];
1059 char grp_str
[INET_ADDRSTRLEN
];
1060 char old_addr_str
[INET_ADDRSTRLEN
];
1061 char new_addr_str
[INET_ADDRSTRLEN
];
1062 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1063 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1064 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
, old_addr_str
, sizeof(old_addr_str
));
1065 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
, new_addr_str
, sizeof(new_addr_str
));
1066 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1067 __PRETTY_FUNCTION__
,
1068 src_str
, grp_str
, ch
->interface
->name
,
1069 ch
->ifassert_my_metric
.rpt_bit_flag
,
1070 ch
->ifassert_my_metric
.metric_preference
,
1071 ch
->ifassert_my_metric
.route_metric
,
1073 my_metric_new
.rpt_bit_flag
,
1074 my_metric_new
.metric_preference
,
1075 my_metric_new
.route_metric
,
1079 ch
->ifassert_my_metric
= my_metric_new
;
1081 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1082 &ch
->ifassert_winner_metric
)) {
1083 assert_action_a5(ch
);
1087 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1089 int old_atd
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1090 int new_atd
= PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1092 if (new_atd
== old_atd
)
1095 if (PIM_DEBUG_PIM_EVENTS
) {
1096 char src_str
[INET_ADDRSTRLEN
];
1097 char grp_str
[INET_ADDRSTRLEN
];
1098 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1099 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1100 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1101 __PRETTY_FUNCTION__
,
1102 src_str
, grp_str
, ch
->interface
->name
,
1107 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1108 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1111 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1112 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1114 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1115 assert_action_a5(ch
);
1121 * If we have a new pim interface, check to
1122 * see if any of the pre-existing channels have
1123 * their upstream out that way and turn on forwarding
1124 * for that ifchannel then.
1127 pim_ifchannel_scan_forward_start (struct interface
*new_ifp
)
1129 struct listnode
*ifnode
;
1130 struct interface
*ifp
;
1131 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1133 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), ifnode
, ifp
))
1135 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1136 struct listnode
*ch_node
;
1137 struct pim_ifchannel
*ch
;
1142 if (new_pim_ifp
== loop_pim_ifp
)
1145 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp
->pim_ifchannel_list
, ch_node
, ch
))
1147 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
1149 struct pim_upstream
*up
= ch
->upstream
;
1150 if ((!up
->channel_oil
) &&
1151 (up
->rpf
.source_nexthop
.interface
== new_ifp
))
1152 pim_forward_start (ch
);
1159 * Downstream per-interface (S,G,rpt) state machine
1160 * states that we need to move (S,G,rpt) items
1161 * into different states at the start of the
1162 * reception of a *,G join as well, when
1163 * we get End of Message
1166 pim_ifchannel_set_star_g_join_state (struct pim_ifchannel
*ch
, int eom
)
1168 struct pim_ifchannel
*child
;
1169 struct listnode
*ch_node
;
1171 if (PIM_DEBUG_PIM_TRACE
)
1172 zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__
,
1173 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
),
1178 for (ALL_LIST_ELEMENTS_RO (ch
->sources
, ch_node
, child
))
1180 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1183 switch (child
->ifjoin_state
)
1185 case PIM_IFJOIN_NOINFO
:
1186 case PIM_IFJOIN_JOIN
:
1188 case PIM_IFJOIN_PRUNE
:
1190 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1192 case PIM_IFJOIN_PRUNE_PENDING
:
1194 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING_TMP
;
1196 case PIM_IFJOIN_PRUNE_TMP
:
1197 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1199 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;