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 * A (*,G) or a (*,*) is going away
46 * remove the parent pointer from
47 * those pointing at us
50 pim_ifchannel_remove_children (struct pim_ifchannel
*ch
)
52 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
53 struct listnode
*ch_node
;
54 struct pim_ifchannel
*child
;
56 // Basic sanity, (*,*) not currently supported
57 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
) &&
58 (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
61 // Basic sanity (S,G) have no children
62 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
63 (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
66 for (ALL_LIST_ELEMENTS_RO (pim_ifp
->pim_ifchannel_list
, ch_node
, child
))
68 if (child
->parent
== ch
)
74 * A (*,G) or a (*,*) is being created
75 * find all the children that would point
79 pim_ifchannel_find_new_children (struct pim_ifchannel
*ch
)
81 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
82 struct pim_ifchannel
*child
;
83 struct listnode
*ch_node
;
85 // Basic Sanity that we are not being silly
86 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
87 (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
90 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
) &&
91 (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
94 for (ALL_LIST_ELEMENTS_RO (pim_ifp
->pim_ifchannel_list
, ch_node
, child
))
96 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
) &&
97 (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
) &&
103 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
105 zassert(!ch
->t_ifjoin_expiry_timer
);
106 zassert(!ch
->t_ifjoin_prune_pending_timer
);
107 zassert(!ch
->t_ifassert_timer
);
109 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
112 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
114 struct pim_interface
*pim_ifp
;
116 pim_ifp
= ch
->interface
->info
;
120 * When this channel is removed
121 * we need to find all our children
122 * and make sure our pointers are fixed
124 pim_ifchannel_remove_children (ch
);
126 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
127 pim_upstream_update_join_desired(ch
->upstream
);
130 pim_upstream_del(ch
->upstream
);
133 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
134 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
135 THREAD_OFF(ch
->t_ifassert_timer
);
138 notice that listnode_delete() can't be moved
139 into pim_ifchannel_free() because the later is
140 called by list_delete_all_node()
142 listnode_delete(pim_ifp
->pim_ifchannel_list
, ch
);
144 pim_ifchannel_free(ch
);
147 #define IFCHANNEL_NOINFO(ch) \
149 ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \
151 ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \
153 ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \
156 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
158 if (IFCHANNEL_NOINFO(ch
)) {
160 /* In NOINFO state, timers should have been cleared */
161 zassert(!ch
->t_ifjoin_expiry_timer
);
162 zassert(!ch
->t_ifjoin_prune_pending_timer
);
163 zassert(!ch
->t_ifassert_timer
);
165 pim_ifchannel_delete(ch
);
169 void pim_ifchannel_ifjoin_switch(const char *caller
,
170 struct pim_ifchannel
*ch
,
171 enum pim_ifjoin_state new_state
)
173 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
175 if (PIM_DEBUG_PIM_EVENTS
)
176 zlog_debug ("PIM_IFCHANNEL: %s is switching from %s to %s",
177 pim_str_sg_dump (&ch
->sg
),
178 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
),
179 pim_ifchannel_ifjoin_name (new_state
));
182 if (old_state
== new_state
) {
183 if (PIM_DEBUG_PIM_EVENTS
) {
184 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
185 __PRETTY_FUNCTION__
, caller
, new_state
,
186 pim_ifchannel_ifjoin_name(new_state
));
191 ch
->ifjoin_state
= new_state
;
193 /* Transition to/from NOINFO ? */
195 (old_state
== PIM_IFJOIN_NOINFO
)
197 (new_state
== PIM_IFJOIN_NOINFO
)
200 if (PIM_DEBUG_PIM_EVENTS
) {
201 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
202 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN" : "UP"),
203 pim_str_sg_dump (&ch
->sg
), ch
->interface
->name
);
207 Record uptime of state transition to/from NOINFO
209 ch
->ifjoin_creation
= pim_time_monotonic_sec();
211 pim_upstream_update_join_desired(ch
->upstream
);
212 pim_ifchannel_update_could_assert(ch
);
213 pim_ifchannel_update_assert_tracking_desired(ch
);
217 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
)
219 switch (ifjoin_state
) {
220 case PIM_IFJOIN_NOINFO
: return "NOINFO";
221 case PIM_IFJOIN_JOIN
: return "JOIN";
222 case PIM_IFJOIN_PRUNE_PENDING
: return "PRUNEP";
225 return "ifjoin_bad_state";
228 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
230 switch (ifassert_state
) {
231 case PIM_IFASSERT_NOINFO
: return "NOINFO";
232 case PIM_IFASSERT_I_AM_WINNER
: return "WINNER";
233 case PIM_IFASSERT_I_AM_LOSER
: return "LOSER";
236 return "ifassert_bad_state";
240 RFC 4601: 4.6.5. Assert State Macros
242 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
243 defaults to Infinity when in the NoInfo state.
245 void reset_ifassert_state(struct pim_ifchannel
*ch
)
247 THREAD_OFF(ch
->t_ifassert_timer
);
249 pim_ifassert_winner_set(ch
,
252 qpim_infinite_assert_metric
);
255 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
256 struct prefix_sg
*sg
)
258 struct pim_interface
*pim_ifp
;
259 struct listnode
*ch_node
;
260 struct pim_ifchannel
*ch
;
267 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
269 pim_str_sg_dump (sg
),
274 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
276 (sg
->src
.s_addr
== ch
->sg
.src
.s_addr
) &&
277 (sg
->grp
.s_addr
== ch
->sg
.grp
.s_addr
)
286 static void ifmembership_set(struct pim_ifchannel
*ch
,
287 enum pim_ifmembership membership
)
289 if (ch
->local_ifmembership
== membership
)
292 if (PIM_DEBUG_PIM_EVENTS
) {
293 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
295 pim_str_sg_dump (&ch
->sg
),
296 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE" : "NOINFO",
297 ch
->interface
->name
);
300 ch
->local_ifmembership
= membership
;
302 pim_upstream_update_join_desired(ch
->upstream
);
303 pim_ifchannel_update_could_assert(ch
);
304 pim_ifchannel_update_assert_tracking_desired(ch
);
308 void pim_ifchannel_membership_clear(struct interface
*ifp
)
310 struct pim_interface
*pim_ifp
;
311 struct listnode
*ch_node
;
312 struct pim_ifchannel
*ch
;
317 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
318 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
322 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
324 struct pim_interface
*pim_ifp
;
325 struct listnode
*node
;
326 struct listnode
*next_node
;
327 struct pim_ifchannel
*ch
;
332 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
, ch
)) {
333 delete_on_noinfo(ch
);
338 * For a given Interface, if we are given a S,G
339 * Find the *,G (If we have it).
340 * If we are passed a *,G, find the *,* ifchannel
343 static struct pim_ifchannel
*
344 pim_ifchannel_find_parent (struct interface
*ifp
,
345 struct prefix_sg
*sg
)
347 struct prefix_sg parent_sg
= *sg
;
350 if (((sg
->src
.s_addr
== INADDR_ANY
) &&
351 (sg
->grp
.s_addr
== INADDR_ANY
)) ||
352 ((sg
->src
.s_addr
!= INADDR_ANY
) &&
353 (sg
->grp
.s_addr
== INADDR_ANY
)))
357 if ((sg
->src
.s_addr
!= INADDR_ANY
) &&
358 (sg
->grp
.s_addr
!= INADDR_ANY
))
360 parent_sg
.src
.s_addr
= INADDR_ANY
;
361 return pim_ifchannel_find (ifp
, &parent_sg
);
364 // (*,G) -- Not going to find anything currently
365 parent_sg
.grp
.s_addr
= INADDR_ANY
;
366 return pim_ifchannel_find (ifp
, &parent_sg
);
369 struct pim_ifchannel
*
370 pim_ifchannel_add(struct interface
*ifp
,
371 struct prefix_sg
*sg
, int flags
)
373 struct pim_interface
*pim_ifp
;
374 struct pim_ifchannel
*ch
;
375 struct pim_upstream
*up
;
377 ch
= pim_ifchannel_find(ifp
, sg
);
384 up
= pim_upstream_add(sg
, NULL
, flags
);
386 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
388 pim_str_sg_dump (sg
), ifp
->name
);
392 ch
= XMALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
394 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
396 pim_str_sg_dump (sg
), ifp
->name
);
405 ch
->parent
= pim_ifchannel_find_parent (ifp
, sg
);
406 pim_ifchannel_find_new_children (ch
);
407 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
409 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
410 ch
->t_ifjoin_expiry_timer
= NULL
;
411 ch
->t_ifjoin_prune_pending_timer
= NULL
;
412 ch
->ifjoin_creation
= 0;
414 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
415 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval (ch
);
417 ch
->ifassert_winner
.s_addr
= 0;
420 ch
->t_ifassert_timer
= NULL
;
421 reset_ifassert_state(ch
);
422 if (pim_macro_ch_could_assert_eval(ch
))
423 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
425 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
427 if (pim_macro_assert_tracking_desired_eval(ch
))
428 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
430 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
433 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
435 zassert(IFCHANNEL_NOINFO(ch
));
440 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
442 pim_forward_stop(ch
);
443 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
444 delete_on_noinfo(ch
);
447 static int on_ifjoin_expiry_timer(struct thread
*t
)
449 struct pim_ifchannel
*ch
;
455 ch
->t_ifjoin_expiry_timer
= NULL
;
457 zassert(ch
->ifjoin_state
== PIM_IFJOIN_JOIN
);
459 ifjoin_to_noinfo(ch
);
460 /* ch may have been deleted */
465 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
467 struct pim_ifchannel
*ch
;
468 int send_prune_echo
; /* boolean */
469 struct interface
*ifp
;
470 struct pim_interface
*pim_ifp
;
477 ch
->t_ifjoin_prune_pending_timer
= NULL
;
479 zassert(ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
);
481 /* Send PruneEcho(S,G) ? */
484 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
489 ifjoin_to_noinfo(ch
);
490 /* from here ch may have been deleted */
493 pim_joinprune_send (ifp
, pim_ifp
->primary_address
,
499 static void check_recv_upstream(int is_join
,
500 struct interface
*recv_ifp
,
501 struct in_addr upstream
,
502 struct prefix_sg
*sg
,
503 uint8_t source_flags
,
506 struct pim_upstream
*up
;
508 /* Upstream (S,G) in Joined state ? */
509 up
= pim_upstream_find(sg
);
512 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
515 /* Upstream (S,G) in Joined state */
517 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
518 /* RPF'(S,G) not found */
519 zlog_warn("%s %s: RPF'%s not found",
520 __FILE__
, __PRETTY_FUNCTION__
,
521 pim_str_sg_dump (sg
));
525 /* upstream directed to RPF'(S,G) ? */
526 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
529 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
530 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
, sizeof(rpf_str
));
531 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
532 __FILE__
, __PRETTY_FUNCTION__
,
533 pim_str_sg_dump (sg
),
534 up_str
, rpf_str
, recv_ifp
->name
);
537 /* upstream directed to RPF'(S,G) */
540 /* Join(S,G) to RPF'(S,G) */
541 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
, holdtime
);
545 /* Prune to RPF'(S,G) */
547 if (source_flags
& PIM_RPT_BIT_MASK
) {
548 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
549 /* Prune(*,G) to RPF'(S,G) */
550 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
551 up
, up
->rpf
.rpf_addr
.u
.prefix4
);
555 /* Prune(S,G,rpt) to RPF'(S,G) */
556 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
557 up
, up
->rpf
.rpf_addr
.u
.prefix4
);
561 /* Prune(S,G) to RPF'(S,G) */
562 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
,
563 up
->rpf
.rpf_addr
.u
.prefix4
);
566 static int nonlocal_upstream(int is_join
,
567 struct interface
*recv_ifp
,
568 struct in_addr upstream
,
569 struct prefix_sg
*sg
,
570 uint8_t source_flags
,
573 struct pim_interface
*recv_pim_ifp
;
574 int is_local
; /* boolean */
576 recv_pim_ifp
= recv_ifp
->info
;
577 zassert(recv_pim_ifp
);
579 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
581 if (PIM_DEBUG_PIM_TRACE
) {
583 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
584 zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s",
586 is_join
? "join" : "prune",
587 pim_str_sg_dump (sg
),
588 is_local
? "local" : "non-local",
589 up_str
, recv_ifp
->name
);
596 Since recv upstream addr was not directed to our primary
597 address, check if we should react to it in any way.
599 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
,
600 source_flags
, holdtime
);
602 return 1; /* non-local */
605 void pim_ifchannel_join_add(struct interface
*ifp
,
606 struct in_addr neigh_addr
,
607 struct in_addr upstream
,
608 struct prefix_sg
*sg
,
609 uint8_t source_flags
,
612 struct pim_interface
*pim_ifp
;
613 struct pim_ifchannel
*ch
;
615 if (nonlocal_upstream(1 /* join */, ifp
, upstream
,
616 sg
, source_flags
, holdtime
)) {
620 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
625 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
627 Transitions from "I am Assert Loser" State
629 Receive Join(S,G) on Interface I
631 We receive a Join(S,G) that has the Upstream Neighbor Address
632 field set to my primary IP address on interface I. The action is
633 to transition to NoInfo state, delete this (S,G) assert state
634 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
637 Notice: The nonlocal_upstream() test above ensures the upstream
638 address of the join message is our primary address.
640 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
642 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
, sizeof(neigh_str
));
643 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
645 pim_str_sg_dump (sg
), neigh_str
, ifp
->name
);
647 assert_action_a5(ch
);
653 switch (ch
->ifjoin_state
) {
654 case PIM_IFJOIN_NOINFO
:
655 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
656 if (pim_macro_chisin_oiflist(ch
)) {
657 pim_upstream_inherited_olist (ch
->upstream
);
658 pim_forward_start(ch
);
661 case PIM_IFJOIN_JOIN
:
662 zassert(!ch
->t_ifjoin_prune_pending_timer
);
665 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
666 previously received join message with holdtime=0xFFFF.
668 if (ch
->t_ifjoin_expiry_timer
) {
669 unsigned long remain
=
670 thread_timer_remain_second(ch
->t_ifjoin_expiry_timer
);
671 if (remain
> holdtime
) {
673 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
675 Transitions from Join State
677 The (S,G) downstream state machine on interface I remains in
678 Join state, and the Expiry Timer (ET) is restarted, set to
679 maximum of its current value and the HoldTime from the
680 triggering Join/Prune message.
682 Conclusion: Do not change the ET if the current value is
683 higher than the received join holdtime.
688 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
690 case PIM_IFJOIN_PRUNE_PENDING
:
691 zassert(!ch
->t_ifjoin_expiry_timer
);
692 zassert(ch
->t_ifjoin_prune_pending_timer
);
693 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
694 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
698 zassert(!IFCHANNEL_NOINFO(ch
));
700 if (holdtime
!= 0xFFFF) {
701 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
702 on_ifjoin_expiry_timer
,
707 void pim_ifchannel_prune(struct interface
*ifp
,
708 struct in_addr upstream
,
709 struct prefix_sg
*sg
,
710 uint8_t source_flags
,
713 struct pim_ifchannel
*ch
;
714 int jp_override_interval_msec
;
716 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
,
717 sg
, source_flags
, holdtime
)) {
721 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
725 switch (ch
->ifjoin_state
) {
726 case PIM_IFJOIN_NOINFO
:
727 case PIM_IFJOIN_PRUNE_PENDING
:
730 case PIM_IFJOIN_JOIN
:
732 struct pim_interface
*pim_ifp
;
736 zassert(ch
->t_ifjoin_expiry_timer
);
737 zassert(!ch
->t_ifjoin_prune_pending_timer
);
739 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
741 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_PRUNE_PENDING
);
743 if (listcount(pim_ifp
->pim_neighbor_list
) > 1) {
744 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
747 jp_override_interval_msec
= 0; /* schedule to expire immediately */
748 /* If we called ifjoin_prune() directly instead, care should
749 be taken not to use "ch" afterwards since it would be
753 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
754 on_ifjoin_prune_pending_timer
,
755 ch
, jp_override_interval_msec
);
757 zassert(!ch
->t_ifjoin_expiry_timer
);
758 zassert(ch
->t_ifjoin_prune_pending_timer
);
765 void pim_ifchannel_local_membership_add(struct interface
*ifp
,
766 struct prefix_sg
*sg
)
768 struct pim_ifchannel
*ch
;
769 struct pim_interface
*pim_ifp
;
771 /* PIM enabled on interface? */
775 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
778 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
783 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
785 zassert(!IFCHANNEL_NOINFO(ch
));
787 if (sg
->src
.s_addr
== INADDR_ANY
)
789 struct pim_upstream
*up
= pim_upstream_find (sg
);
790 struct pim_upstream
*child
;
791 struct listnode
*up_node
;
793 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list
, up_node
, child
))
795 if (child
->parent
== up
)
797 if (PIM_DEBUG_EVENTS
)
801 strcpy (buff
, pim_str_sg_dump (&up
->sg
));
802 zlog_debug("%s %s: IGMP (S,G)=%s from %s",
803 __FILE__
, __PRETTY_FUNCTION__
,
804 buff
, pim_str_sg_dump (sg
));
807 if (pim_upstream_evaluate_join_desired (child
))
809 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
810 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
817 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
818 struct prefix_sg
*sg
)
820 struct pim_ifchannel
*ch
;
821 struct pim_interface
*pim_ifp
;
823 /* PIM enabled on interface? */
827 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
830 ch
= pim_ifchannel_find(ifp
, sg
);
834 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
836 if (sg
->src
.s_addr
== INADDR_ANY
)
838 struct pim_upstream
*up
= pim_upstream_find (sg
);
839 struct pim_upstream
*child
;
840 struct listnode
*up_node
;
842 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list
, up_node
, child
))
844 if (child
->parent
== up
)
846 struct channel_oil
*c_oil
= child
->channel_oil
;
847 struct pim_ifchannel
*ch
= pim_ifchannel_find (ifp
, &child
->sg
);
848 struct pim_interface
*pim_ifp
= ifp
->info
;
850 if (PIM_DEBUG_EVENTS
)
853 strcpy (buff
, pim_str_sg_dump (&up
->sg
));
854 zlog_debug("%s %s: Prune(S,G)=%s from %s",
855 __FILE__
, __PRETTY_FUNCTION__
,
856 buff
, pim_str_sg_dump (&child
->sg
));
859 if (!pim_upstream_evaluate_join_desired (child
))
860 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
863 * If the S,G has no if channel and the c_oil still
864 * has output here then the *,G was supplying the implied
865 * if channel. So remove it.
867 if (!ch
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
868 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
872 delete_on_noinfo(ch
);
875 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
877 int old_couldassert
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
878 int new_couldassert
= PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
880 if (new_couldassert
== old_couldassert
)
883 if (PIM_DEBUG_PIM_EVENTS
) {
886 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
887 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
888 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
890 src_str
, grp_str
, ch
->interface
->name
,
891 old_couldassert
, new_couldassert
);
894 if (new_couldassert
) {
895 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
896 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
899 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
900 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
902 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
903 assert_action_a4(ch
);
907 pim_ifchannel_update_my_assert_metric(ch
);
911 my_assert_metric may be affected by:
914 pim_ifp->primary_address
915 rpf->source_nexthop.mrib_metric_preference;
916 rpf->source_nexthop.mrib_route_metric;
918 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
920 struct pim_assert_metric my_metric_new
= pim_macro_ch_my_assert_metric_eval(ch
);
922 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
925 if (PIM_DEBUG_PIM_EVENTS
) {
928 char old_addr_str
[100];
929 char new_addr_str
[100];
930 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
931 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
932 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
, old_addr_str
, sizeof(old_addr_str
));
933 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
, new_addr_str
, sizeof(new_addr_str
));
934 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
936 src_str
, grp_str
, ch
->interface
->name
,
937 ch
->ifassert_my_metric
.rpt_bit_flag
,
938 ch
->ifassert_my_metric
.metric_preference
,
939 ch
->ifassert_my_metric
.route_metric
,
941 my_metric_new
.rpt_bit_flag
,
942 my_metric_new
.metric_preference
,
943 my_metric_new
.route_metric
,
947 ch
->ifassert_my_metric
= my_metric_new
;
949 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
950 &ch
->ifassert_winner_metric
)) {
951 assert_action_a5(ch
);
955 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
957 int old_atd
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
958 int new_atd
= PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
960 if (new_atd
== old_atd
)
963 if (PIM_DEBUG_PIM_EVENTS
) {
966 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
967 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
968 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
970 src_str
, grp_str
, ch
->interface
->name
,
975 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
976 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
979 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
980 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
982 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
983 assert_action_a5(ch
);
989 * If we have a new pim interface, check to
990 * see if any of the pre-existing channels have
991 * their upstream out that way and turn on forwarding
992 * for that ifchannel then.
995 pim_ifchannel_scan_forward_start (struct interface
*new_ifp
)
997 struct listnode
*ifnode
;
998 struct interface
*ifp
;
999 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1001 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), ifnode
, ifp
))
1003 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1004 struct listnode
*ch_node
;
1005 struct pim_ifchannel
*ch
;
1010 if (new_pim_ifp
== loop_pim_ifp
)
1013 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp
->pim_ifchannel_list
, ch_node
, ch
))
1015 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
1017 struct pim_upstream
*up
= ch
->upstream
;
1018 if ((!up
->channel_oil
) &&
1019 (up
->rpf
.source_nexthop
.interface
== new_ifp
))
1020 pim_forward_start (ch
);