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,
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"
46 pim_ifchannel_compare (struct pim_ifchannel
*ch1
, struct pim_ifchannel
*ch2
)
48 struct pim_interface
*pim_ifp1
;
49 struct pim_interface
*pim_ifp2
;
51 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
54 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
57 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
60 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
63 pim_ifp1
= ch1
->interface
->info
;
64 pim_ifp2
= ch2
->interface
->info
;
65 if (ntohl(pim_ifp1
->primary_address
.s_addr
) < ntohl(pim_ifp2
->primary_address
.s_addr
))
68 if (ntohl(pim_ifp1
->primary_address
.s_addr
) > ntohl(pim_ifp2
->primary_address
.s_addr
))
71 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
74 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
81 * A (*,G) or a (*,*) is going away
82 * remove the parent pointer from
83 * those pointing at us
86 pim_ifchannel_remove_children (struct pim_ifchannel
*ch
)
88 struct pim_ifchannel
*child
;
93 while (!list_isempty (ch
->sources
))
95 child
= listnode_head (ch
->sources
);
97 listnode_delete (ch
->sources
, child
);
102 * A (*,G) or a (*,*) is being created
103 * find all the children that would point
107 pim_ifchannel_find_new_children (struct pim_ifchannel
*ch
)
109 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
110 struct pim_ifchannel
*child
;
111 struct listnode
*ch_node
;
113 // Basic Sanity that we are not being silly
114 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
115 (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
118 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
) &&
119 (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
122 for (ALL_LIST_ELEMENTS_RO (pim_ifp
->pim_ifchannel_list
, ch_node
, child
))
124 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
) &&
125 (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
) &&
129 listnode_add_sort (ch
->sources
, child
);
134 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
136 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
139 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
141 struct pim_interface
*pim_ifp
;
143 pim_ifp
= ch
->interface
->info
;
145 if (ch
->upstream
->channel_oil
)
147 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
148 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
149 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
151 pim_channel_del_oif (ch
->upstream
->channel_oil
, ch
->interface
, mask
);
153 * Do we have any S,G's that are inheriting?
154 * Nuke from on high too.
156 if (ch
->upstream
->sources
)
158 struct pim_upstream
*child
;
159 struct listnode
*up_node
;
161 for (ALL_LIST_ELEMENTS_RO (ch
->upstream
->sources
, up_node
, child
))
162 pim_channel_del_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
167 * When this channel is removed
168 * we need to find all our children
169 * and make sure our pointers are fixed
171 pim_ifchannel_remove_children (ch
);
174 list_delete (ch
->sources
);
176 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
177 pim_upstream_update_join_desired(ch
->upstream
);
180 pim_upstream_del(ch
->upstream
, __PRETTY_FUNCTION__
);
183 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
184 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
185 THREAD_OFF(ch
->t_ifassert_timer
);
189 listnode_delete (ch
->parent
->sources
, ch
);
193 notice that listnode_delete() can't be moved
194 into pim_ifchannel_free() because the later is
195 called by list_delete_all_node()
197 listnode_delete(pim_ifp
->pim_ifchannel_list
, ch
);
198 hash_release(pim_ifp
->pim_ifchannel_hash
, ch
);
199 listnode_delete(pim_ifchannel_list
, ch
);
201 pim_ifchannel_free(ch
);
205 pim_ifchannel_delete_all (struct interface
*ifp
)
207 struct pim_interface
*pim_ifp
;
208 struct listnode
*ifchannel_node
;
209 struct listnode
*ifchannel_nextnode
;
210 struct pim_ifchannel
*ifchannel
;
216 for (ALL_LIST_ELEMENTS (pim_ifp
->pim_ifchannel_list
, ifchannel_node
,
217 ifchannel_nextnode
, ifchannel
))
219 pim_ifchannel_delete (ifchannel
);
223 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
225 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
&&
226 ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
&&
227 ch
->t_ifjoin_expiry_timer
== NULL
)
228 pim_ifchannel_delete(ch
);
232 void pim_ifchannel_ifjoin_switch(const char *caller
,
233 struct pim_ifchannel
*ch
,
234 enum pim_ifjoin_state new_state
)
236 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
238 if (PIM_DEBUG_PIM_EVENTS
)
239 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
242 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
),
243 pim_ifchannel_ifjoin_name (new_state
));
246 if (old_state
== new_state
) {
247 if (PIM_DEBUG_PIM_EVENTS
) {
248 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
249 __PRETTY_FUNCTION__
, caller
, new_state
,
250 pim_ifchannel_ifjoin_name(new_state
));
255 ch
->ifjoin_state
= new_state
;
257 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
259 struct pim_upstream
*up
= ch
->upstream
;
260 struct pim_upstream
*child
;
261 struct listnode
*up_node
;
265 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
)
267 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
269 struct channel_oil
*c_oil
= child
->channel_oil
;
270 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
272 if (PIM_DEBUG_PIM_TRACE
)
273 zlog_debug("%s %s: Prune(S,G)=%s from %s",
274 __FILE__
, __PRETTY_FUNCTION__
,
275 child
->sg_str
, up
->sg_str
);
279 if (!pim_upstream_evaluate_join_desired (child
))
281 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
282 pim_upstream_update_join_desired (child
);
286 * If the S,G has no if channel and the c_oil still
287 * has output here then the *,G was supplying the implied
288 * if channel. So remove it.
289 * I think this is dead code now. is it?
291 if (!ch
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
292 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
295 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
297 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
299 if (PIM_DEBUG_PIM_TRACE
)
300 zlog_debug("%s %s: Join(S,G)=%s from %s",
301 __FILE__
, __PRETTY_FUNCTION__
,
302 child
->sg_str
, up
->sg_str
);
304 if (pim_upstream_evaluate_join_desired (child
))
306 pim_channel_add_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
307 pim_upstream_update_join_desired (child
);
313 /* Transition to/from NOINFO ? */
314 if ((old_state
== PIM_IFJOIN_NOINFO
) ||
315 (new_state
== PIM_IFJOIN_NOINFO
)) {
317 if (PIM_DEBUG_PIM_EVENTS
) {
318 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
319 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN" : "UP"),
320 ch
->sg_str
, ch
->interface
->name
);
324 Record uptime of state transition to/from NOINFO
326 ch
->ifjoin_creation
= pim_time_monotonic_sec();
328 pim_upstream_update_join_desired(ch
->upstream
);
329 pim_ifchannel_update_could_assert(ch
);
330 pim_ifchannel_update_assert_tracking_desired(ch
);
334 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
)
336 switch (ifjoin_state
) {
337 case PIM_IFJOIN_NOINFO
: return "NOINFO";
338 case PIM_IFJOIN_JOIN
: return "JOIN";
339 case PIM_IFJOIN_PRUNE
: return "PRUNE";
340 case PIM_IFJOIN_PRUNE_PENDING
: return "PRUNEP";
341 case PIM_IFJOIN_PRUNE_TMP
: return "PRUNET";
342 case PIM_IFJOIN_PRUNE_PENDING_TMP
: return "PRUNEPT";
345 return "ifjoin_bad_state";
348 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
350 switch (ifassert_state
) {
351 case PIM_IFASSERT_NOINFO
: return "NOINFO";
352 case PIM_IFASSERT_I_AM_WINNER
: return "WINNER";
353 case PIM_IFASSERT_I_AM_LOSER
: return "LOSER";
356 return "ifassert_bad_state";
360 RFC 4601: 4.6.5. Assert State Macros
362 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
363 defaults to Infinity when in the NoInfo state.
365 void reset_ifassert_state(struct pim_ifchannel
*ch
)
367 THREAD_OFF(ch
->t_ifassert_timer
);
369 pim_ifassert_winner_set(ch
,
372 qpim_infinite_assert_metric
);
375 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
376 struct prefix_sg
*sg
)
378 struct pim_interface
*pim_ifp
;
379 struct pim_ifchannel
*ch
;
380 struct pim_ifchannel lookup
;
385 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
387 pim_str_sg_dump (sg
),
393 ch
= hash_lookup (pim_ifp
->pim_ifchannel_hash
, &lookup
);
398 static void ifmembership_set(struct pim_ifchannel
*ch
,
399 enum pim_ifmembership membership
)
401 if (ch
->local_ifmembership
== membership
)
404 if (PIM_DEBUG_PIM_EVENTS
) {
405 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
408 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE" : "NOINFO",
409 ch
->interface
->name
);
412 ch
->local_ifmembership
= membership
;
414 pim_upstream_update_join_desired(ch
->upstream
);
415 pim_ifchannel_update_could_assert(ch
);
416 pim_ifchannel_update_assert_tracking_desired(ch
);
420 void pim_ifchannel_membership_clear(struct interface
*ifp
)
422 struct pim_interface
*pim_ifp
;
423 struct listnode
*ch_node
;
424 struct pim_ifchannel
*ch
;
429 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
430 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
434 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
436 struct pim_interface
*pim_ifp
;
437 struct listnode
*node
;
438 struct listnode
*next_node
;
439 struct pim_ifchannel
*ch
;
444 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
, ch
)) {
445 delete_on_noinfo(ch
);
450 * For a given Interface, if we are given a S,G
451 * Find the *,G (If we have it).
452 * If we are passed a *,G, find the *,* ifchannel
455 static struct pim_ifchannel
*
456 pim_ifchannel_find_parent (struct pim_ifchannel
*ch
)
458 struct prefix_sg parent_sg
= ch
->sg
;
459 struct pim_ifchannel
*parent
= NULL
;
462 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
) &&
463 (parent_sg
.grp
.s_addr
!= INADDR_ANY
))
465 parent_sg
.src
.s_addr
= INADDR_ANY
;
466 parent
= pim_ifchannel_find (ch
->interface
, &parent_sg
);
469 listnode_add (parent
->sources
, ch
);
476 struct pim_ifchannel
*
477 pim_ifchannel_add(struct interface
*ifp
,
478 struct prefix_sg
*sg
, int flags
)
480 struct pim_interface
*pim_ifp
;
481 struct pim_ifchannel
*ch
;
482 struct pim_upstream
*up
;
484 ch
= pim_ifchannel_find(ifp
, sg
);
490 up
= pim_upstream_add(sg
, NULL
, flags
, __PRETTY_FUNCTION__
);
492 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
494 pim_str_sg_dump (sg
), ifp
->name
);
498 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
500 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
502 up
->sg_str
, ifp
->name
);
504 pim_upstream_del (up
, __PRETTY_FUNCTION__
);
512 pim_str_sg_set (sg
, ch
->sg_str
);
513 ch
->parent
= pim_ifchannel_find_parent (ch
);
514 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
516 ch
->sources
= list_new ();
517 ch
->sources
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
522 pim_ifchannel_find_new_children (ch
);
523 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
525 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
526 ch
->t_ifjoin_expiry_timer
= NULL
;
527 ch
->t_ifjoin_prune_pending_timer
= NULL
;
528 ch
->ifjoin_creation
= 0;
530 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
531 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval (ch
);
533 ch
->ifassert_winner
.s_addr
= 0;
536 ch
->t_ifassert_timer
= NULL
;
537 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
538 reset_ifassert_state(ch
);
539 if (pim_macro_ch_could_assert_eval(ch
))
540 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
542 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
544 if (pim_macro_assert_tracking_desired_eval(ch
))
545 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
547 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
550 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
551 ch
= hash_get (pim_ifp
->pim_ifchannel_hash
, ch
, hash_alloc_intern
);
552 listnode_add_sort(pim_ifchannel_list
, ch
);
557 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
559 pim_forward_stop(ch
);
560 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
561 delete_on_noinfo(ch
);
564 static int on_ifjoin_expiry_timer(struct thread
*t
)
566 struct pim_ifchannel
*ch
;
570 ch
->t_ifjoin_expiry_timer
= NULL
;
572 ifjoin_to_noinfo(ch
);
573 /* ch may have been deleted */
578 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
580 struct pim_ifchannel
*ch
;
581 int send_prune_echo
; /* boolean */
582 struct interface
*ifp
;
583 struct pim_interface
*pim_ifp
;
587 ch
->t_ifjoin_prune_pending_timer
= NULL
;
589 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
)
591 /* Send PruneEcho(S,G) ? */
594 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
596 ifjoin_to_noinfo(ch
);
597 /* from here ch may have been deleted */
603 rpf
.source_nexthop
.interface
= ifp
;
604 rpf
.rpf_addr
.u
.prefix4
= pim_ifp
->primary_address
;
605 pim_joinprune_send (&rpf
, ch
->upstream
, 0);
610 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
611 __PRETTY_FUNCTION__
, pim_str_sg_dump (&ch
->sg
),
612 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
));
618 static void check_recv_upstream(int is_join
,
619 struct interface
*recv_ifp
,
620 struct in_addr upstream
,
621 struct prefix_sg
*sg
,
622 uint8_t source_flags
,
625 struct pim_upstream
*up
;
627 /* Upstream (S,G) in Joined state ? */
628 up
= pim_upstream_find(sg
);
631 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
634 /* Upstream (S,G) in Joined state */
636 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
637 /* RPF'(S,G) not found */
638 zlog_warn("%s %s: RPF'%s not found",
639 __FILE__
, __PRETTY_FUNCTION__
,
644 /* upstream directed to RPF'(S,G) ? */
645 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
646 char up_str
[INET_ADDRSTRLEN
];
647 char rpf_str
[PREFIX_STRLEN
];
648 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
649 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
, sizeof(rpf_str
));
650 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
651 __FILE__
, __PRETTY_FUNCTION__
,
653 up_str
, rpf_str
, recv_ifp
->name
);
656 /* upstream directed to RPF'(S,G) */
659 /* Join(S,G) to RPF'(S,G) */
660 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
, holdtime
);
664 /* Prune to RPF'(S,G) */
666 if (source_flags
& PIM_RPT_BIT_MASK
) {
667 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
668 /* Prune(*,G) to RPF'(S,G) */
669 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", up
);
673 /* Prune(S,G,rpt) to RPF'(S,G) */
674 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", up
);
678 /* Prune(S,G) to RPF'(S,G) */
679 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
683 nonlocal_upstream(int is_join
,
684 struct interface
*recv_ifp
,
685 struct in_addr upstream
,
686 struct prefix_sg
*sg
,
687 uint8_t source_flags
,
690 struct pim_interface
*recv_pim_ifp
;
691 int is_local
; /* boolean */
693 recv_pim_ifp
= recv_ifp
->info
;
694 zassert(recv_pim_ifp
);
696 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
701 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
702 char up_str
[INET_ADDRSTRLEN
];
703 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
704 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
706 is_join
? "join" : "prune",
707 pim_str_sg_dump (sg
),
708 up_str
, recv_ifp
->name
);
712 * Since recv upstream addr was not directed to our primary
713 * address, check if we should react to it in any way.
715 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
,
716 source_flags
, holdtime
);
718 return 1; /* non-local */
721 void pim_ifchannel_join_add(struct interface
*ifp
,
722 struct in_addr neigh_addr
,
723 struct in_addr upstream
,
724 struct prefix_sg
*sg
,
725 uint8_t source_flags
,
728 struct pim_interface
*pim_ifp
;
729 struct pim_ifchannel
*ch
;
731 if (nonlocal_upstream(1 /* join */, ifp
, upstream
,
732 sg
, source_flags
, holdtime
)) {
736 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
741 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
743 Transitions from "I am Assert Loser" State
745 Receive Join(S,G) on Interface I
747 We receive a Join(S,G) that has the Upstream Neighbor Address
748 field set to my primary IP address on interface I. The action is
749 to transition to NoInfo state, delete this (S,G) assert state
750 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
753 Notice: The nonlocal_upstream() test above ensures the upstream
754 address of the join message is our primary address.
756 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
757 char neigh_str
[INET_ADDRSTRLEN
];
758 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
, sizeof(neigh_str
));
759 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
761 ch
->sg_str
, neigh_str
, ifp
->name
);
763 assert_action_a5(ch
);
769 switch (ch
->ifjoin_state
) {
770 case PIM_IFJOIN_NOINFO
:
771 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
772 if (pim_macro_chisin_oiflist(ch
)) {
773 pim_upstream_inherited_olist (ch
->upstream
);
774 pim_forward_start(ch
);
777 case PIM_IFJOIN_JOIN
:
778 zassert(!ch
->t_ifjoin_prune_pending_timer
);
781 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
782 previously received join message with holdtime=0xFFFF.
784 if (ch
->t_ifjoin_expiry_timer
) {
785 unsigned long remain
=
786 thread_timer_remain_second(ch
->t_ifjoin_expiry_timer
);
787 if (remain
> holdtime
) {
789 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
791 Transitions from Join State
793 The (S,G) downstream state machine on interface I remains in
794 Join state, and the Expiry Timer (ET) is restarted, set to
795 maximum of its current value and the HoldTime from the
796 triggering Join/Prune message.
798 Conclusion: Do not change the ET if the current value is
799 higher than the received join holdtime.
804 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
806 case PIM_IFJOIN_PRUNE
:
807 if (source_flags
& PIM_ENCODE_RPT_BIT
)
808 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
810 case PIM_IFJOIN_PRUNE_PENDING
:
811 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
812 if (source_flags
& PIM_ENCODE_RPT_BIT
)
814 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
815 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
818 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
820 case PIM_IFJOIN_PRUNE_TMP
:
822 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
826 if (holdtime
!= 0xFFFF) {
827 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
828 on_ifjoin_expiry_timer
,
833 void pim_ifchannel_prune(struct interface
*ifp
,
834 struct in_addr upstream
,
835 struct prefix_sg
*sg
,
836 uint8_t source_flags
,
839 struct pim_ifchannel
*ch
;
840 struct pim_interface
*pim_ifp
;
841 int jp_override_interval_msec
;
843 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
,
844 sg
, source_flags
, holdtime
)) {
848 ch
= pim_ifchannel_find (ifp
, sg
);
849 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
))
852 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
853 __PRETTY_FUNCTION__
, ifp
->name
, pim_str_sg_dump (sg
), source_flags
);
857 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
863 switch (ch
->ifjoin_state
) {
864 case PIM_IFJOIN_NOINFO
:
865 if (source_flags
& PIM_ENCODE_RPT_BIT
)
867 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
868 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
869 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
870 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
872 jp_override_interval_msec
= 0; /* schedule to expire immediately */
873 /* If we called ifjoin_prune() directly instead, care should
874 be taken not to use "ch" afterwards since it would be
877 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
878 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
879 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
880 on_ifjoin_prune_pending_timer
,
881 ch
, jp_override_interval_msec
);
882 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
883 on_ifjoin_expiry_timer
,
887 case PIM_IFJOIN_PRUNE_PENDING
:
890 case PIM_IFJOIN_JOIN
:
891 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
893 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_PRUNE_PENDING
);
895 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
896 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
898 jp_override_interval_msec
= 0; /* schedule to expire immediately */
899 /* If we called ifjoin_prune() directly instead, care should
900 be taken not to use "ch" afterwards since it would be
902 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
903 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
904 on_ifjoin_prune_pending_timer
,
905 ch
, jp_override_interval_msec
);
907 case PIM_IFJOIN_PRUNE
:
908 if (source_flags
& PIM_ENCODE_RPT_BIT
)
910 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
911 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
912 on_ifjoin_expiry_timer
,
916 case PIM_IFJOIN_PRUNE_TMP
:
917 if (source_flags
& PIM_ENCODE_RPT_BIT
)
919 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
920 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
921 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
922 on_ifjoin_expiry_timer
,
926 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
927 if (source_flags
& PIM_ENCODE_RPT_BIT
)
929 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
930 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
931 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
932 on_ifjoin_expiry_timer
,
940 pim_ifchannel_local_membership_add(struct interface
*ifp
,
941 struct prefix_sg
*sg
)
943 struct pim_ifchannel
*ch
;
944 struct pim_interface
*pim_ifp
;
946 /* PIM enabled on interface? */
950 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
953 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
958 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
960 if (sg
->src
.s_addr
== INADDR_ANY
)
962 struct pim_upstream
*up
= pim_upstream_find (sg
);
963 struct pim_upstream
*child
;
964 struct listnode
*up_node
;
966 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
968 if (PIM_DEBUG_EVENTS
)
969 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
970 __FILE__
, __PRETTY_FUNCTION__
,
971 child
->sg_str
, ifp
->name
, up
->sg_str
);
973 if (pim_upstream_evaluate_join_desired_interface (child
, ch
))
975 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
976 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
984 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
985 struct prefix_sg
*sg
)
987 struct pim_ifchannel
*ch
;
988 struct pim_interface
*pim_ifp
;
990 /* PIM enabled on interface? */
994 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
997 ch
= pim_ifchannel_find(ifp
, sg
);
1001 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1003 if (sg
->src
.s_addr
== INADDR_ANY
)
1005 struct pim_upstream
*up
= pim_upstream_find (sg
);
1006 struct pim_upstream
*child
;
1007 struct listnode
*up_node
;
1009 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
1011 struct channel_oil
*c_oil
= child
->channel_oil
;
1012 struct pim_ifchannel
*chchannel
= pim_ifchannel_find (ifp
, &child
->sg
);
1013 struct pim_interface
*pim_ifp
= ifp
->info
;
1015 if (PIM_DEBUG_EVENTS
)
1016 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1017 __FILE__
, __PRETTY_FUNCTION__
,
1018 up
->sg_str
, ifp
->name
, child
->sg_str
);
1020 if (c_oil
&& !pim_upstream_evaluate_join_desired_interface (child
, ch
))
1021 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1024 * If the S,G has no if channel and the c_oil still
1025 * has output here then the *,G was supplying the implied
1026 * if channel. So remove it.
1028 if (!chchannel
&& c_oil
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1029 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1032 delete_on_noinfo(ch
);
1035 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1037 int old_couldassert
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1038 int new_couldassert
= PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1040 if (new_couldassert
== old_couldassert
)
1043 if (PIM_DEBUG_PIM_EVENTS
) {
1044 char src_str
[INET_ADDRSTRLEN
];
1045 char grp_str
[INET_ADDRSTRLEN
];
1046 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1047 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1048 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1049 __PRETTY_FUNCTION__
,
1050 src_str
, grp_str
, ch
->interface
->name
,
1051 old_couldassert
, new_couldassert
);
1054 if (new_couldassert
) {
1055 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1056 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1059 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1060 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1062 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1063 assert_action_a4(ch
);
1067 pim_ifchannel_update_my_assert_metric(ch
);
1071 my_assert_metric may be affected by:
1074 pim_ifp->primary_address
1075 rpf->source_nexthop.mrib_metric_preference;
1076 rpf->source_nexthop.mrib_route_metric;
1078 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1080 struct pim_assert_metric my_metric_new
= pim_macro_ch_my_assert_metric_eval(ch
);
1082 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1085 if (PIM_DEBUG_PIM_EVENTS
) {
1086 char src_str
[INET_ADDRSTRLEN
];
1087 char grp_str
[INET_ADDRSTRLEN
];
1088 char old_addr_str
[INET_ADDRSTRLEN
];
1089 char new_addr_str
[INET_ADDRSTRLEN
];
1090 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1091 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1092 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
, old_addr_str
, sizeof(old_addr_str
));
1093 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
, new_addr_str
, sizeof(new_addr_str
));
1094 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1095 __PRETTY_FUNCTION__
,
1096 src_str
, grp_str
, ch
->interface
->name
,
1097 ch
->ifassert_my_metric
.rpt_bit_flag
,
1098 ch
->ifassert_my_metric
.metric_preference
,
1099 ch
->ifassert_my_metric
.route_metric
,
1101 my_metric_new
.rpt_bit_flag
,
1102 my_metric_new
.metric_preference
,
1103 my_metric_new
.route_metric
,
1107 ch
->ifassert_my_metric
= my_metric_new
;
1109 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1110 &ch
->ifassert_winner_metric
)) {
1111 assert_action_a5(ch
);
1115 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1117 int old_atd
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1118 int new_atd
= PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1120 if (new_atd
== old_atd
)
1123 if (PIM_DEBUG_PIM_EVENTS
) {
1124 char src_str
[INET_ADDRSTRLEN
];
1125 char grp_str
[INET_ADDRSTRLEN
];
1126 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1127 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1128 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1129 __PRETTY_FUNCTION__
,
1130 src_str
, grp_str
, ch
->interface
->name
,
1135 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1136 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1139 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1140 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1142 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1143 assert_action_a5(ch
);
1149 * If we have a new pim interface, check to
1150 * see if any of the pre-existing channels have
1151 * their upstream out that way and turn on forwarding
1152 * for that ifchannel then.
1155 pim_ifchannel_scan_forward_start (struct interface
*new_ifp
)
1157 struct listnode
*ifnode
;
1158 struct interface
*ifp
;
1159 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1161 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), ifnode
, ifp
))
1163 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1164 struct listnode
*ch_node
;
1165 struct pim_ifchannel
*ch
;
1170 if (new_pim_ifp
== loop_pim_ifp
)
1173 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp
->pim_ifchannel_list
, ch_node
, ch
))
1175 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
1177 struct pim_upstream
*up
= ch
->upstream
;
1178 if ((!up
->channel_oil
) &&
1179 (up
->rpf
.source_nexthop
.interface
== new_ifp
))
1180 pim_forward_start (ch
);
1187 * Downstream per-interface (S,G,rpt) state machine
1188 * states that we need to move (S,G,rpt) items
1189 * into different states at the start of the
1190 * reception of a *,G join as well, when
1191 * we get End of Message
1194 pim_ifchannel_set_star_g_join_state (struct pim_ifchannel
*ch
, int eom
)
1196 struct pim_ifchannel
*child
;
1197 struct listnode
*ch_node
;
1199 if (PIM_DEBUG_PIM_TRACE
)
1200 zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__
,
1201 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
),
1206 for (ALL_LIST_ELEMENTS_RO (ch
->sources
, ch_node
, child
))
1208 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1211 switch (child
->ifjoin_state
)
1213 case PIM_IFJOIN_NOINFO
:
1214 case PIM_IFJOIN_JOIN
:
1216 case PIM_IFJOIN_PRUNE
:
1218 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1220 case PIM_IFJOIN_PRUNE_PENDING
:
1222 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING_TMP
;
1224 case PIM_IFJOIN_PRUNE_TMP
:
1225 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1227 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1234 pim_ifchannel_hash_key (void *arg
)
1236 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1238 return jhash_2words (ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);
1242 pim_ifchannel_equal (const void *arg1
, const void *arg2
)
1244 const struct pim_ifchannel
*ch1
= (const struct pim_ifchannel
*)arg1
;
1245 const struct pim_ifchannel
*ch2
= (const struct pim_ifchannel
*)arg2
;
1247 if ((ch1
->sg
.grp
.s_addr
== ch2
->sg
.grp
.s_addr
) &&
1248 (ch1
->sg
.src
.s_addr
== ch2
->sg
.src
.s_addr
))