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,
31 #include "pim_iface.h"
32 #include "pim_ifchannel.h"
33 #include "pim_zebra.h"
39 #include "pim_macro.h"
41 #include "pim_upstream.h"
44 pim_ifchannel_compare (struct pim_ifchannel
*ch1
, struct pim_ifchannel
*ch2
)
46 struct pim_interface
*pim_ifp1
;
47 struct pim_interface
*pim_ifp2
;
49 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
52 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
55 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
58 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
61 pim_ifp1
= ch1
->interface
->info
;
62 pim_ifp2
= ch2
->interface
->info
;
63 if (ntohl(pim_ifp1
->primary_address
.s_addr
) < ntohl(pim_ifp2
->primary_address
.s_addr
))
66 if (ntohl(pim_ifp1
->primary_address
.s_addr
) > ntohl(pim_ifp2
->primary_address
.s_addr
))
69 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
72 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
79 * A (*,G) or a (*,*) is going away
80 * remove the parent pointer from
81 * those pointing at us
84 pim_ifchannel_remove_children (struct pim_ifchannel
*ch
)
86 struct pim_ifchannel
*child
;
91 while (!list_isempty (ch
->sources
))
93 child
= listnode_head (ch
->sources
);
95 listnode_delete (ch
->sources
, child
);
100 * A (*,G) or a (*,*) is being created
101 * find all the children that would point
105 pim_ifchannel_find_new_children (struct pim_ifchannel
*ch
)
107 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
108 struct pim_ifchannel
*child
;
109 struct listnode
*ch_node
;
111 // Basic Sanity that we are not being silly
112 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
113 (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
116 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
) &&
117 (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
120 for (ALL_LIST_ELEMENTS_RO (pim_ifp
->pim_ifchannel_list
, ch_node
, child
))
122 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
) &&
123 (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
) &&
127 listnode_add_sort (ch
->sources
, child
);
132 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
134 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
137 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
139 struct pim_interface
*pim_ifp
;
141 pim_ifp
= ch
->interface
->info
;
143 if (ch
->upstream
->channel_oil
)
145 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
146 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
147 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
149 pim_channel_del_oif (ch
->upstream
->channel_oil
, ch
->interface
, mask
);
151 * Do we have any S,G's that are inheriting?
152 * Nuke from on high too.
154 if (ch
->upstream
->sources
)
156 struct pim_upstream
*child
;
157 struct listnode
*up_node
;
159 for (ALL_LIST_ELEMENTS_RO (ch
->upstream
->sources
, up_node
, child
))
160 pim_channel_del_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
165 * When this channel is removed
166 * we need to find all our children
167 * and make sure our pointers are fixed
169 pim_ifchannel_remove_children (ch
);
172 list_delete (ch
->sources
);
174 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
175 pim_upstream_update_join_desired(ch
->upstream
);
178 pim_upstream_del(ch
->upstream
, __PRETTY_FUNCTION__
);
181 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
182 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
183 THREAD_OFF(ch
->t_ifassert_timer
);
187 listnode_delete (ch
->parent
->sources
, ch
);
191 notice that listnode_delete() can't be moved
192 into pim_ifchannel_free() because the later is
193 called by list_delete_all_node()
195 listnode_delete(pim_ifp
->pim_ifchannel_list
, ch
);
196 listnode_delete(pim_ifchannel_list
, ch
);
198 pim_ifchannel_free(ch
);
202 pim_ifchannel_delete_all (struct interface
*ifp
)
204 struct pim_interface
*pim_ifp
;
205 struct listnode
*ifchannel_node
;
206 struct listnode
*ifchannel_nextnode
;
207 struct pim_ifchannel
*ifchannel
;
213 for (ALL_LIST_ELEMENTS (pim_ifp
->pim_ifchannel_list
, ifchannel_node
,
214 ifchannel_nextnode
, ifchannel
))
216 pim_ifchannel_delete (ifchannel
);
220 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
222 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
&&
223 ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
&&
224 ch
->t_ifjoin_expiry_timer
== NULL
)
225 pim_ifchannel_delete(ch
);
229 void pim_ifchannel_ifjoin_switch(const char *caller
,
230 struct pim_ifchannel
*ch
,
231 enum pim_ifjoin_state new_state
)
233 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
235 if (PIM_DEBUG_PIM_EVENTS
)
236 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
239 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
),
240 pim_ifchannel_ifjoin_name (new_state
));
243 if (old_state
== new_state
) {
244 if (PIM_DEBUG_PIM_EVENTS
) {
245 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
246 __PRETTY_FUNCTION__
, caller
, new_state
,
247 pim_ifchannel_ifjoin_name(new_state
));
252 ch
->ifjoin_state
= new_state
;
254 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
256 struct pim_upstream
*up
= ch
->upstream
;
257 struct pim_upstream
*child
;
258 struct listnode
*up_node
;
262 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
)
264 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
266 struct channel_oil
*c_oil
= child
->channel_oil
;
267 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
269 if (PIM_DEBUG_PIM_TRACE
)
270 zlog_debug("%s %s: Prune(S,G)=%s from %s",
271 __FILE__
, __PRETTY_FUNCTION__
,
272 child
->sg_str
, up
->sg_str
);
276 if (!pim_upstream_evaluate_join_desired (child
))
278 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
279 pim_upstream_update_join_desired (child
);
283 * If the S,G has no if channel and the c_oil still
284 * has output here then the *,G was supplying the implied
285 * if channel. So remove it.
286 * I think this is dead code now. is it?
288 if (!ch
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
289 pim_channel_del_oif (c_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
292 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
294 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
296 if (PIM_DEBUG_PIM_TRACE
)
297 zlog_debug("%s %s: Join(S,G)=%s from %s",
298 __FILE__
, __PRETTY_FUNCTION__
,
299 child
->sg_str
, up
->sg_str
);
301 if (pim_upstream_evaluate_join_desired (child
))
303 pim_channel_add_oif (child
->channel_oil
, ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
304 pim_upstream_update_join_desired (child
);
310 /* Transition to/from NOINFO ? */
311 if ((old_state
== PIM_IFJOIN_NOINFO
) ||
312 (new_state
== PIM_IFJOIN_NOINFO
)) {
314 if (PIM_DEBUG_PIM_EVENTS
) {
315 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
316 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN" : "UP"),
317 ch
->sg_str
, ch
->interface
->name
);
321 Record uptime of state transition to/from NOINFO
323 ch
->ifjoin_creation
= pim_time_monotonic_sec();
325 pim_upstream_update_join_desired(ch
->upstream
);
326 pim_ifchannel_update_could_assert(ch
);
327 pim_ifchannel_update_assert_tracking_desired(ch
);
331 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
)
333 switch (ifjoin_state
) {
334 case PIM_IFJOIN_NOINFO
: return "NOINFO";
335 case PIM_IFJOIN_JOIN
: return "JOIN";
336 case PIM_IFJOIN_PRUNE
: return "PRUNE";
337 case PIM_IFJOIN_PRUNE_PENDING
: return "PRUNEP";
338 case PIM_IFJOIN_PRUNE_TMP
: return "PRUNET";
339 case PIM_IFJOIN_PRUNE_PENDING_TMP
: return "PRUNEPT";
342 return "ifjoin_bad_state";
345 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
347 switch (ifassert_state
) {
348 case PIM_IFASSERT_NOINFO
: return "NOINFO";
349 case PIM_IFASSERT_I_AM_WINNER
: return "WINNER";
350 case PIM_IFASSERT_I_AM_LOSER
: return "LOSER";
353 return "ifassert_bad_state";
357 RFC 4601: 4.6.5. Assert State Macros
359 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
360 defaults to Infinity when in the NoInfo state.
362 void reset_ifassert_state(struct pim_ifchannel
*ch
)
364 THREAD_OFF(ch
->t_ifassert_timer
);
366 pim_ifassert_winner_set(ch
,
369 qpim_infinite_assert_metric
);
372 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
373 struct prefix_sg
*sg
)
375 struct pim_interface
*pim_ifp
;
376 struct listnode
*ch_node
;
377 struct pim_ifchannel
*ch
;
384 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
386 pim_str_sg_dump (sg
),
391 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
393 (sg
->src
.s_addr
== ch
->sg
.src
.s_addr
) &&
394 (sg
->grp
.s_addr
== ch
->sg
.grp
.s_addr
)
403 static void ifmembership_set(struct pim_ifchannel
*ch
,
404 enum pim_ifmembership membership
)
406 if (ch
->local_ifmembership
== membership
)
409 if (PIM_DEBUG_PIM_EVENTS
) {
410 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
413 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE" : "NOINFO",
414 ch
->interface
->name
);
417 ch
->local_ifmembership
= membership
;
419 pim_upstream_update_join_desired(ch
->upstream
);
420 pim_ifchannel_update_could_assert(ch
);
421 pim_ifchannel_update_assert_tracking_desired(ch
);
425 void pim_ifchannel_membership_clear(struct interface
*ifp
)
427 struct pim_interface
*pim_ifp
;
428 struct listnode
*ch_node
;
429 struct pim_ifchannel
*ch
;
434 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
435 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
439 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
441 struct pim_interface
*pim_ifp
;
442 struct listnode
*node
;
443 struct listnode
*next_node
;
444 struct pim_ifchannel
*ch
;
449 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
, ch
)) {
450 delete_on_noinfo(ch
);
455 * For a given Interface, if we are given a S,G
456 * Find the *,G (If we have it).
457 * If we are passed a *,G, find the *,* ifchannel
460 static struct pim_ifchannel
*
461 pim_ifchannel_find_parent (struct pim_ifchannel
*ch
)
463 struct prefix_sg parent_sg
= ch
->sg
;
464 struct pim_ifchannel
*parent
= NULL
;
467 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
) &&
468 (parent_sg
.grp
.s_addr
!= INADDR_ANY
))
470 parent_sg
.src
.s_addr
= INADDR_ANY
;
471 parent
= pim_ifchannel_find (ch
->interface
, &parent_sg
);
474 listnode_add (parent
->sources
, ch
);
481 struct pim_ifchannel
*
482 pim_ifchannel_add(struct interface
*ifp
,
483 struct prefix_sg
*sg
, int flags
)
485 struct pim_interface
*pim_ifp
;
486 struct pim_ifchannel
*ch
;
487 struct pim_upstream
*up
;
489 ch
= pim_ifchannel_find(ifp
, sg
);
495 up
= pim_upstream_add(sg
, NULL
, flags
, __PRETTY_FUNCTION__
);
497 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
499 pim_str_sg_dump (sg
), ifp
->name
);
503 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
505 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
507 up
->sg_str
, ifp
->name
);
509 pim_upstream_del (up
, __PRETTY_FUNCTION__
);
517 pim_str_sg_set (sg
, ch
->sg_str
);
518 ch
->parent
= pim_ifchannel_find_parent (ch
);
519 if (ch
->sg
.src
.s_addr
== INADDR_ANY
)
521 ch
->sources
= list_new ();
522 ch
->sources
->cmp
= (int (*)(void *, void *))pim_ifchannel_compare
;
527 pim_ifchannel_find_new_children (ch
);
528 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
530 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
531 ch
->t_ifjoin_expiry_timer
= NULL
;
532 ch
->t_ifjoin_prune_pending_timer
= NULL
;
533 ch
->ifjoin_creation
= 0;
535 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
536 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval (ch
);
538 ch
->ifassert_winner
.s_addr
= 0;
541 ch
->t_ifassert_timer
= NULL
;
542 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
543 reset_ifassert_state(ch
);
544 if (pim_macro_ch_could_assert_eval(ch
))
545 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
547 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
549 if (pim_macro_assert_tracking_desired_eval(ch
))
550 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
552 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
555 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
556 listnode_add_sort(pim_ifchannel_list
, ch
);
561 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
563 pim_forward_stop(ch
);
564 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
565 delete_on_noinfo(ch
);
568 static int on_ifjoin_expiry_timer(struct thread
*t
)
570 struct pim_ifchannel
*ch
;
574 ch
->t_ifjoin_expiry_timer
= NULL
;
576 ifjoin_to_noinfo(ch
);
577 /* ch may have been deleted */
582 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
584 struct pim_ifchannel
*ch
;
585 int send_prune_echo
; /* boolean */
586 struct interface
*ifp
;
587 struct pim_interface
*pim_ifp
;
591 ch
->t_ifjoin_prune_pending_timer
= NULL
;
593 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
)
595 /* Send PruneEcho(S,G) ? */
598 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
600 ifjoin_to_noinfo(ch
);
601 /* from here ch may have been deleted */
604 pim_joinprune_send (ifp
, pim_ifp
->primary_address
,
609 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
610 __PRETTY_FUNCTION__
, pim_str_sg_dump (&ch
->sg
),
611 pim_ifchannel_ifjoin_name (ch
->ifjoin_state
));
617 static void check_recv_upstream(int is_join
,
618 struct interface
*recv_ifp
,
619 struct in_addr upstream
,
620 struct prefix_sg
*sg
,
621 uint8_t source_flags
,
624 struct pim_upstream
*up
;
626 /* Upstream (S,G) in Joined state ? */
627 up
= pim_upstream_find(sg
);
630 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
633 /* Upstream (S,G) in Joined state */
635 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
636 /* RPF'(S,G) not found */
637 zlog_warn("%s %s: RPF'%s not found",
638 __FILE__
, __PRETTY_FUNCTION__
,
643 /* upstream directed to RPF'(S,G) ? */
644 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
645 char up_str
[INET_ADDRSTRLEN
];
646 char rpf_str
[PREFIX_STRLEN
];
647 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
648 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
, sizeof(rpf_str
));
649 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
650 __FILE__
, __PRETTY_FUNCTION__
,
652 up_str
, rpf_str
, recv_ifp
->name
);
655 /* upstream directed to RPF'(S,G) */
658 /* Join(S,G) to RPF'(S,G) */
659 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
, holdtime
);
663 /* Prune to RPF'(S,G) */
665 if (source_flags
& PIM_RPT_BIT_MASK
) {
666 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
667 /* Prune(*,G) to RPF'(S,G) */
668 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
669 up
, up
->rpf
.rpf_addr
.u
.prefix4
);
673 /* Prune(S,G,rpt) to RPF'(S,G) */
674 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
675 up
, up
->rpf
.rpf_addr
.u
.prefix4
);
679 /* Prune(S,G) to RPF'(S,G) */
680 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
,
681 up
->rpf
.rpf_addr
.u
.prefix4
);
684 static int nonlocal_upstream(int is_join
,
685 struct interface
*recv_ifp
,
686 struct in_addr upstream
,
687 struct prefix_sg
*sg
,
688 uint8_t source_flags
,
691 struct pim_interface
*recv_pim_ifp
;
692 int is_local
; /* boolean */
694 recv_pim_ifp
= recv_ifp
->info
;
695 zassert(recv_pim_ifp
);
697 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
699 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
700 char up_str
[INET_ADDRSTRLEN
];
701 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
702 zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s",
704 is_join
? "join" : "prune",
705 pim_str_sg_dump (sg
),
706 is_local
? "local" : "non-local",
707 up_str
, recv_ifp
->name
);
714 Since recv upstream addr was not directed to our primary
715 address, check if we should react to it in any way.
717 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
,
718 source_flags
, holdtime
);
720 return 1; /* non-local */
723 void pim_ifchannel_join_add(struct interface
*ifp
,
724 struct in_addr neigh_addr
,
725 struct in_addr upstream
,
726 struct prefix_sg
*sg
,
727 uint8_t source_flags
,
730 struct pim_interface
*pim_ifp
;
731 struct pim_ifchannel
*ch
;
733 if (nonlocal_upstream(1 /* join */, ifp
, upstream
,
734 sg
, source_flags
, holdtime
)) {
738 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
743 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
745 Transitions from "I am Assert Loser" State
747 Receive Join(S,G) on Interface I
749 We receive a Join(S,G) that has the Upstream Neighbor Address
750 field set to my primary IP address on interface I. The action is
751 to transition to NoInfo state, delete this (S,G) assert state
752 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
755 Notice: The nonlocal_upstream() test above ensures the upstream
756 address of the join message is our primary address.
758 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
759 char neigh_str
[INET_ADDRSTRLEN
];
760 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
, sizeof(neigh_str
));
761 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
763 ch
->sg_str
, neigh_str
, ifp
->name
);
765 assert_action_a5(ch
);
771 switch (ch
->ifjoin_state
) {
772 case PIM_IFJOIN_NOINFO
:
773 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
774 if (pim_macro_chisin_oiflist(ch
)) {
775 pim_upstream_inherited_olist (ch
->upstream
);
776 pim_forward_start(ch
);
779 case PIM_IFJOIN_JOIN
:
780 zassert(!ch
->t_ifjoin_prune_pending_timer
);
783 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
784 previously received join message with holdtime=0xFFFF.
786 if (ch
->t_ifjoin_expiry_timer
) {
787 unsigned long remain
=
788 thread_timer_remain_second(ch
->t_ifjoin_expiry_timer
);
789 if (remain
> holdtime
) {
791 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
793 Transitions from Join State
795 The (S,G) downstream state machine on interface I remains in
796 Join state, and the Expiry Timer (ET) is restarted, set to
797 maximum of its current value and the HoldTime from the
798 triggering Join/Prune message.
800 Conclusion: Do not change the ET if the current value is
801 higher than the received join holdtime.
806 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
808 case PIM_IFJOIN_PRUNE
:
809 if (source_flags
& PIM_ENCODE_RPT_BIT
)
810 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
812 case PIM_IFJOIN_PRUNE_PENDING
:
813 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
814 if (source_flags
& PIM_ENCODE_RPT_BIT
)
816 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
817 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
820 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_JOIN
);
822 case PIM_IFJOIN_PRUNE_TMP
:
824 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
828 if (holdtime
!= 0xFFFF) {
829 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
830 on_ifjoin_expiry_timer
,
835 void pim_ifchannel_prune(struct interface
*ifp
,
836 struct in_addr upstream
,
837 struct prefix_sg
*sg
,
838 uint8_t source_flags
,
841 struct pim_ifchannel
*ch
;
842 struct pim_interface
*pim_ifp
;
843 int jp_override_interval_msec
;
845 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
,
846 sg
, source_flags
, holdtime
)) {
850 ch
= pim_ifchannel_find (ifp
, sg
);
851 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
))
854 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
855 __PRETTY_FUNCTION__
, ifp
->name
, pim_str_sg_dump (sg
), source_flags
);
859 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
865 switch (ch
->ifjoin_state
) {
866 case PIM_IFJOIN_NOINFO
:
867 if (source_flags
& PIM_ENCODE_RPT_BIT
)
869 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
870 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
871 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
872 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
874 jp_override_interval_msec
= 0; /* schedule to expire immediately */
875 /* If we called ifjoin_prune() directly instead, care should
876 be taken not to use "ch" afterwards since it would be
879 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
880 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
881 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
882 on_ifjoin_prune_pending_timer
,
883 ch
, jp_override_interval_msec
);
884 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
885 on_ifjoin_expiry_timer
,
889 case PIM_IFJOIN_PRUNE_PENDING
:
892 case PIM_IFJOIN_JOIN
:
893 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
895 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_PRUNE_PENDING
);
897 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
898 jp_override_interval_msec
= pim_if_jp_override_interval_msec(ifp
);
900 jp_override_interval_msec
= 0; /* schedule to expire immediately */
901 /* If we called ifjoin_prune() directly instead, care should
902 be taken not to use "ch" afterwards since it would be
904 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
905 THREAD_TIMER_MSEC_ON(master
, ch
->t_ifjoin_prune_pending_timer
,
906 on_ifjoin_prune_pending_timer
,
907 ch
, jp_override_interval_msec
);
909 case PIM_IFJOIN_PRUNE
:
910 if (source_flags
& PIM_ENCODE_RPT_BIT
)
912 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
913 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
914 on_ifjoin_expiry_timer
,
918 case PIM_IFJOIN_PRUNE_TMP
:
919 if (source_flags
& PIM_ENCODE_RPT_BIT
)
921 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
922 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
923 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
924 on_ifjoin_expiry_timer
,
928 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
929 if (source_flags
& PIM_ENCODE_RPT_BIT
)
931 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
932 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
933 THREAD_TIMER_ON(master
, ch
->t_ifjoin_expiry_timer
,
934 on_ifjoin_expiry_timer
,
942 pim_ifchannel_local_membership_add(struct interface
*ifp
,
943 struct prefix_sg
*sg
)
945 struct pim_ifchannel
*ch
;
946 struct pim_interface
*pim_ifp
;
948 /* PIM enabled on interface? */
952 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
955 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
960 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
962 if (sg
->src
.s_addr
== INADDR_ANY
)
964 struct pim_upstream
*up
= pim_upstream_find (sg
);
965 struct pim_upstream
*child
;
966 struct listnode
*up_node
;
968 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
970 if (PIM_DEBUG_EVENTS
)
971 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
972 __FILE__
, __PRETTY_FUNCTION__
,
973 child
->sg_str
, ifp
->name
, up
->sg_str
);
975 if (pim_upstream_evaluate_join_desired (child
))
977 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
978 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
986 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
987 struct prefix_sg
*sg
)
989 struct pim_ifchannel
*ch
;
990 struct pim_interface
*pim_ifp
;
992 /* PIM enabled on interface? */
996 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
999 ch
= pim_ifchannel_find(ifp
, sg
);
1003 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1005 if (sg
->src
.s_addr
== INADDR_ANY
)
1007 struct pim_upstream
*up
= pim_upstream_find (sg
);
1008 struct pim_upstream
*child
;
1009 struct listnode
*up_node
;
1011 for (ALL_LIST_ELEMENTS_RO (up
->sources
, up_node
, child
))
1013 struct channel_oil
*c_oil
= child
->channel_oil
;
1014 struct pim_ifchannel
*chchannel
= pim_ifchannel_find (ifp
, &child
->sg
);
1015 struct pim_interface
*pim_ifp
= ifp
->info
;
1017 if (PIM_DEBUG_EVENTS
)
1018 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1019 __FILE__
, __PRETTY_FUNCTION__
,
1020 up
->sg_str
, ifp
->name
, child
->sg_str
);
1022 if (c_oil
&& !pim_upstream_evaluate_join_desired (child
))
1023 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1026 * If the S,G has no if channel and the c_oil still
1027 * has output here then the *,G was supplying the implied
1028 * if channel. So remove it.
1030 if (!chchannel
&& c_oil
&& c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1031 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_STAR
);
1034 delete_on_noinfo(ch
);
1037 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1039 int old_couldassert
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1040 int new_couldassert
= PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1042 if (new_couldassert
== old_couldassert
)
1045 if (PIM_DEBUG_PIM_EVENTS
) {
1046 char src_str
[INET_ADDRSTRLEN
];
1047 char grp_str
[INET_ADDRSTRLEN
];
1048 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1049 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1050 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1051 __PRETTY_FUNCTION__
,
1052 src_str
, grp_str
, ch
->interface
->name
,
1053 old_couldassert
, new_couldassert
);
1056 if (new_couldassert
) {
1057 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1058 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1061 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1062 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1064 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1065 assert_action_a4(ch
);
1069 pim_ifchannel_update_my_assert_metric(ch
);
1073 my_assert_metric may be affected by:
1076 pim_ifp->primary_address
1077 rpf->source_nexthop.mrib_metric_preference;
1078 rpf->source_nexthop.mrib_route_metric;
1080 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1082 struct pim_assert_metric my_metric_new
= pim_macro_ch_my_assert_metric_eval(ch
);
1084 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1087 if (PIM_DEBUG_PIM_EVENTS
) {
1088 char src_str
[INET_ADDRSTRLEN
];
1089 char grp_str
[INET_ADDRSTRLEN
];
1090 char old_addr_str
[INET_ADDRSTRLEN
];
1091 char new_addr_str
[INET_ADDRSTRLEN
];
1092 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1093 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1094 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
, old_addr_str
, sizeof(old_addr_str
));
1095 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
, new_addr_str
, sizeof(new_addr_str
));
1096 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1097 __PRETTY_FUNCTION__
,
1098 src_str
, grp_str
, ch
->interface
->name
,
1099 ch
->ifassert_my_metric
.rpt_bit_flag
,
1100 ch
->ifassert_my_metric
.metric_preference
,
1101 ch
->ifassert_my_metric
.route_metric
,
1103 my_metric_new
.rpt_bit_flag
,
1104 my_metric_new
.metric_preference
,
1105 my_metric_new
.route_metric
,
1109 ch
->ifassert_my_metric
= my_metric_new
;
1111 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1112 &ch
->ifassert_winner_metric
)) {
1113 assert_action_a5(ch
);
1117 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1119 int old_atd
= PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1120 int new_atd
= PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1122 if (new_atd
== old_atd
)
1125 if (PIM_DEBUG_PIM_EVENTS
) {
1126 char src_str
[INET_ADDRSTRLEN
];
1127 char grp_str
[INET_ADDRSTRLEN
];
1128 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1129 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1130 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1131 __PRETTY_FUNCTION__
,
1132 src_str
, grp_str
, ch
->interface
->name
,
1137 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1138 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1141 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1142 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1144 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1145 assert_action_a5(ch
);
1151 * If we have a new pim interface, check to
1152 * see if any of the pre-existing channels have
1153 * their upstream out that way and turn on forwarding
1154 * for that ifchannel then.
1157 pim_ifchannel_scan_forward_start (struct interface
*new_ifp
)
1159 struct listnode
*ifnode
;
1160 struct interface
*ifp
;
1161 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1163 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), ifnode
, ifp
))
1165 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1166 struct listnode
*ch_node
;
1167 struct pim_ifchannel
*ch
;
1172 if (new_pim_ifp
== loop_pim_ifp
)
1175 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp
->pim_ifchannel_list
, ch_node
, ch
))
1177 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
)
1179 struct pim_upstream
*up
= ch
->upstream
;
1180 if ((!up
->channel_oil
) &&
1181 (up
->rpf
.source_nexthop
.interface
== new_ifp
))
1182 pim_forward_start (ch
);
1189 * Downstream per-interface (S,G,rpt) state machine
1190 * states that we need to move (S,G,rpt) items
1191 * into different states at the start of the
1192 * reception of a *,G join as well, when
1193 * we get End of Message
1196 pim_ifchannel_set_star_g_join_state (struct pim_ifchannel
*ch
, int eom
)
1198 struct pim_ifchannel
*child
;
1199 struct listnode
*ch_node
;
1201 if (PIM_DEBUG_PIM_TRACE
)
1202 zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__
,
1203 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
),
1208 for (ALL_LIST_ELEMENTS_RO (ch
->sources
, ch_node
, child
))
1210 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1213 switch (child
->ifjoin_state
)
1215 case PIM_IFJOIN_NOINFO
:
1216 case PIM_IFJOIN_JOIN
:
1218 case PIM_IFJOIN_PRUNE
:
1220 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1222 case PIM_IFJOIN_PRUNE_PENDING
:
1224 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING_TMP
;
1226 case PIM_IFJOIN_PRUNE_TMP
:
1227 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1229 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;