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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
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 int pim_ifchannel_compare(struct pim_ifchannel
*ch1
, struct pim_ifchannel
*ch2
)
48 struct pim_interface
*pim_ifp1
;
49 struct pim_interface
*pim_ifp2
;
51 pim_ifp1
= ch1
->interface
->info
;
52 pim_ifp2
= ch2
->interface
->info
;
54 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
57 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
60 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
63 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
66 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
69 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
76 * A (*,G) or a (*,*) is going away
77 * remove the parent pointer from
78 * those pointing at us
80 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
82 struct pim_ifchannel
*child
;
87 while (!list_isempty(ch
->sources
)) {
88 child
= listnode_head(ch
->sources
);
90 listnode_delete(ch
->sources
, child
);
95 * A (*,G) or a (*,*) is being created
96 * find all the children that would point
99 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
101 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
102 struct pim_ifchannel
*child
;
103 struct listnode
*ch_node
;
105 // Basic Sanity that we are not being silly
106 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
)
107 && (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
110 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
)
111 && (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
114 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
,
116 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
)
117 && (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
)
120 listnode_add_sort(ch
->sources
, child
);
125 void pim_ifchannel_free(struct pim_ifchannel
*ch
)
127 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
130 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
132 struct pim_interface
*pim_ifp
;
134 pim_ifp
= ch
->interface
->info
;
136 if (ch
->upstream
->channel_oil
) {
137 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
138 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
139 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
141 /* SGRpt entry could have empty oil */
142 if (ch
->upstream
->channel_oil
)
143 pim_channel_del_oif(ch
->upstream
->channel_oil
,
144 ch
->interface
, mask
);
146 * Do we have any S,G's that are inheriting?
147 * Nuke from on high too.
149 if (ch
->upstream
->sources
) {
150 struct pim_upstream
*child
;
151 struct listnode
*up_node
;
153 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
155 pim_channel_del_oif(child
->channel_oil
,
157 PIM_OIF_FLAG_PROTO_STAR
);
162 * When this channel is removed
163 * we need to find all our children
164 * and make sure our pointers are fixed
166 pim_ifchannel_remove_children(ch
);
169 list_delete(ch
->sources
);
171 listnode_delete(ch
->upstream
->ifchannels
, ch
);
173 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
174 pim_upstream_update_join_desired(ch
->upstream
);
177 /* upstream is common across ifchannels, check if upstream's
178 ifchannel list is empty before deleting upstream_del
179 ref count will take care of it.
181 pim_upstream_del(ch
->upstream
, __PRETTY_FUNCTION__
);
184 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
185 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
186 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 if (PIM_DEBUG_PIM_TRACE
)
202 zlog_debug("%s: ifchannel entry %s is deleted ",
203 __PRETTY_FUNCTION__
, ch
->sg_str
);
205 pim_ifchannel_free(ch
);
208 void pim_ifchannel_delete_all(struct interface
*ifp
)
210 struct pim_interface
*pim_ifp
;
211 struct listnode
*ifchannel_node
;
212 struct listnode
*ifchannel_nextnode
;
213 struct pim_ifchannel
*ifchannel
;
219 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, ifchannel_node
,
220 ifchannel_nextnode
, ifchannel
)) {
221 pim_ifchannel_delete(ifchannel
);
225 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
227 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
228 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
229 && ch
->t_ifjoin_expiry_timer
== NULL
)
230 pim_ifchannel_delete(ch
);
233 void pim_ifchannel_ifjoin_switch(const char *caller
, 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
)
240 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
241 ch
->interface
->name
, ch
->sg_str
,
242 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
243 pim_ifchannel_ifjoin_name(new_state
, 0));
246 if (old_state
== new_state
) {
247 if (PIM_DEBUG_PIM_EVENTS
) {
249 "%s calledby %s: non-transition on state %d (%s)",
250 __PRETTY_FUNCTION__
, caller
, new_state
,
251 pim_ifchannel_ifjoin_name(new_state
, 0));
256 ch
->ifjoin_state
= new_state
;
258 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
;
264 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
265 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
267 struct channel_oil
*c_oil
=
269 struct pim_interface
*pim_ifp
=
272 if (PIM_DEBUG_PIM_TRACE
)
274 "%s %s: Prune(S,G)=%s from %s",
282 if (!pim_upstream_evaluate_join_desired(
285 c_oil
, ch
->interface
,
286 PIM_OIF_FLAG_PROTO_STAR
);
287 pim_upstream_update_join_desired(
292 * If the S,G has no if channel and the
294 * has output here then the *,G was
295 * supplying the implied
296 * if channel. So remove it.
297 * I think this is dead code now. is it?
300 && c_oil
->oil
.mfcc_ttls
301 [pim_ifp
->mroute_vif_index
])
303 c_oil
, ch
->interface
,
304 PIM_OIF_FLAG_PROTO_STAR
);
307 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
308 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
310 if (PIM_DEBUG_PIM_TRACE
)
312 "%s %s: Join(S,G)=%s from %s",
318 if (pim_upstream_evaluate_join_desired(
323 PIM_OIF_FLAG_PROTO_STAR
);
324 pim_upstream_update_join_desired(
331 /* Transition to/from NOINFO ? */
332 if ((old_state
== PIM_IFJOIN_NOINFO
)
333 || (new_state
== PIM_IFJOIN_NOINFO
)) {
335 if (PIM_DEBUG_PIM_EVENTS
) {
336 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
337 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
339 ch
->sg_str
, ch
->interface
->name
);
343 Record uptime of state transition to/from NOINFO
345 ch
->ifjoin_creation
= pim_time_monotonic_sec();
347 pim_upstream_update_join_desired(ch
->upstream
);
348 pim_ifchannel_update_could_assert(ch
);
349 pim_ifchannel_update_assert_tracking_desired(ch
);
353 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
356 switch (ifjoin_state
) {
357 case PIM_IFJOIN_NOINFO
:
358 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
363 case PIM_IFJOIN_JOIN
:
366 case PIM_IFJOIN_PRUNE
:
369 case PIM_IFJOIN_PRUNE_PENDING
:
372 case PIM_IFJOIN_PRUNE_TMP
:
375 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
380 return "ifjoin_bad_state";
383 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
385 switch (ifassert_state
) {
386 case PIM_IFASSERT_NOINFO
:
388 case PIM_IFASSERT_I_AM_WINNER
:
390 case PIM_IFASSERT_I_AM_LOSER
:
394 return "ifassert_bad_state";
398 RFC 4601: 4.6.5. Assert State Macros
400 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
401 defaults to Infinity when in the NoInfo state.
403 void reset_ifassert_state(struct pim_ifchannel
*ch
)
405 struct in_addr any
= {.s_addr
= INADDR_ANY
};
407 THREAD_OFF(ch
->t_ifassert_timer
);
409 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
410 qpim_infinite_assert_metric
);
413 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
414 struct prefix_sg
*sg
)
416 struct pim_interface
*pim_ifp
;
417 struct pim_ifchannel
*ch
;
418 struct pim_ifchannel lookup
;
423 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
424 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
429 ch
= hash_lookup(pim_ifp
->pim_ifchannel_hash
, &lookup
);
434 static void ifmembership_set(struct pim_ifchannel
*ch
,
435 enum pim_ifmembership membership
)
437 if (ch
->local_ifmembership
== membership
)
440 if (PIM_DEBUG_PIM_EVENTS
) {
441 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
442 __PRETTY_FUNCTION__
, ch
->sg_str
,
443 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
445 ch
->interface
->name
);
448 ch
->local_ifmembership
= membership
;
450 pim_upstream_update_join_desired(ch
->upstream
);
451 pim_ifchannel_update_could_assert(ch
);
452 pim_ifchannel_update_assert_tracking_desired(ch
);
456 void pim_ifchannel_membership_clear(struct interface
*ifp
)
458 struct pim_interface
*pim_ifp
;
459 struct listnode
*ch_node
;
460 struct pim_ifchannel
*ch
;
465 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_ifchannel_list
, ch_node
, ch
)) {
466 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
470 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
472 struct pim_interface
*pim_ifp
;
473 struct listnode
*node
;
474 struct listnode
*next_node
;
475 struct pim_ifchannel
*ch
;
480 for (ALL_LIST_ELEMENTS(pim_ifp
->pim_ifchannel_list
, node
, next_node
,
482 delete_on_noinfo(ch
);
487 * For a given Interface, if we are given a S,G
488 * Find the *,G (If we have it).
489 * If we are passed a *,G, find the *,* ifchannel
492 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
494 struct prefix_sg parent_sg
= ch
->sg
;
495 struct pim_ifchannel
*parent
= NULL
;
498 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
499 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
500 parent_sg
.src
.s_addr
= INADDR_ANY
;
501 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
504 listnode_add(parent
->sources
, ch
);
511 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
512 struct prefix_sg
*sg
, int flags
)
514 struct pim_interface
*pim_ifp
;
515 struct pim_ifchannel
*ch
;
516 struct pim_upstream
*up
;
518 ch
= pim_ifchannel_find(ifp
, sg
);
524 up
= pim_upstream_add(sg
, NULL
, flags
, __PRETTY_FUNCTION__
);
527 "%s: could not attach upstream (S,G)=%s on interface %s",
528 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
532 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
535 "%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
536 __PRETTY_FUNCTION__
, up
->sg_str
, ifp
->name
);
538 pim_upstream_del(up
, __PRETTY_FUNCTION__
);
546 pim_str_sg_set(sg
, ch
->sg_str
);
547 ch
->parent
= pim_ifchannel_find_parent(ch
);
548 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
549 ch
->sources
= list_new();
551 (int (*)(void *, void *))pim_ifchannel_compare
;
555 pim_ifchannel_find_new_children(ch
);
556 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
558 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
559 ch
->t_ifjoin_expiry_timer
= NULL
;
560 ch
->t_ifjoin_prune_pending_timer
= NULL
;
561 ch
->ifjoin_creation
= 0;
563 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
564 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
566 ch
->ifassert_winner
.s_addr
= 0;
569 ch
->t_ifassert_timer
= NULL
;
570 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
571 reset_ifassert_state(ch
);
572 if (pim_macro_ch_could_assert_eval(ch
))
573 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
575 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
577 if (pim_macro_assert_tracking_desired_eval(ch
))
578 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
580 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
583 listnode_add_sort(pim_ifp
->pim_ifchannel_list
, ch
);
584 ch
= hash_get(pim_ifp
->pim_ifchannel_hash
, ch
, hash_alloc_intern
);
585 listnode_add_sort(pim_ifchannel_list
, ch
);
587 listnode_add_sort(up
->ifchannels
, ch
);
589 if (PIM_DEBUG_PIM_TRACE
)
590 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
596 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
598 pim_forward_stop(ch
);
599 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
601 delete_on_noinfo(ch
);
604 static int on_ifjoin_expiry_timer(struct thread
*t
)
606 struct pim_ifchannel
*ch
;
610 ifjoin_to_noinfo(ch
, true);
611 /* ch may have been deleted */
616 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
618 struct pim_ifchannel
*ch
;
619 int send_prune_echo
; /* boolean */
620 struct interface
*ifp
;
621 struct pim_interface
*pim_ifp
;
625 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
626 /* Send PruneEcho(S,G) ? */
629 send_prune_echo
= (listcount(pim_ifp
->pim_neighbor_list
) > 1);
631 if (send_prune_echo
) {
634 rpf
.source_nexthop
.interface
= ifp
;
635 rpf
.rpf_addr
.u
.prefix4
= pim_ifp
->primary_address
;
636 pim_jp_agg_single_upstream_send(&rpf
, ch
->upstream
, 0);
638 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
639 message on RP path upon prune timer expiry.
641 if (PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
643 pim_upstream_update_join_desired(ch
->upstream
);
645 ch->ifjoin_state transition to NOINFO state
646 ch_del is set to 0 for not deleteing from here.
647 Holdtime expiry (ch_del set to 1) delete the entry.
649 ifjoin_to_noinfo(ch
, false);
651 ifjoin_to_noinfo(ch
, true);
652 /* from here ch may have been deleted */
655 "%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
656 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
657 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
663 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
664 struct in_addr upstream
, struct prefix_sg
*sg
,
665 uint8_t source_flags
, int holdtime
)
667 struct pim_upstream
*up
;
669 /* Upstream (S,G) in Joined state ? */
670 up
= pim_upstream_find(sg
);
673 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
676 /* Upstream (S,G) in Joined state */
678 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
679 /* RPF'(S,G) not found */
680 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
681 __PRETTY_FUNCTION__
, up
->sg_str
);
685 /* upstream directed to RPF'(S,G) ? */
686 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
687 char up_str
[INET_ADDRSTRLEN
];
688 char rpf_str
[PREFIX_STRLEN
];
689 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
690 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
693 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
694 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
695 rpf_str
, recv_ifp
->name
);
698 /* upstream directed to RPF'(S,G) */
701 /* Join(S,G) to RPF'(S,G) */
702 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
707 /* Prune to RPF'(S,G) */
709 if (source_flags
& PIM_RPT_BIT_MASK
) {
710 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
711 /* Prune(*,G) to RPF'(S,G) */
712 pim_upstream_join_timer_decrease_to_t_override(
717 /* Prune(S,G,rpt) to RPF'(S,G) */
718 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
723 /* Prune(S,G) to RPF'(S,G) */
724 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
727 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
728 struct in_addr upstream
, struct prefix_sg
*sg
,
729 uint8_t source_flags
, uint16_t holdtime
)
731 struct pim_interface
*recv_pim_ifp
;
732 int is_local
; /* boolean */
734 recv_pim_ifp
= recv_ifp
->info
;
735 zassert(recv_pim_ifp
);
737 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
742 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
743 char up_str
[INET_ADDRSTRLEN
];
744 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
745 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
746 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
747 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
751 * Since recv upstream addr was not directed to our primary
752 * address, check if we should react to it in any way.
754 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
757 return 1; /* non-local */
760 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
761 struct in_addr upstream
, struct prefix_sg
*sg
,
762 uint8_t source_flags
, uint16_t holdtime
)
764 struct pim_interface
*pim_ifp
;
765 struct pim_ifchannel
*ch
;
767 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
772 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
777 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
779 Transitions from "I am Assert Loser" State
781 Receive Join(S,G) on Interface I
783 We receive a Join(S,G) that has the Upstream Neighbor Address
784 field set to my primary IP address on interface I. The action is
785 to transition to NoInfo state, delete this (S,G) assert state
786 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
789 Notice: The nonlocal_upstream() test above ensures the upstream
790 address of the join message is our primary address.
792 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
793 char neigh_str
[INET_ADDRSTRLEN
];
794 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
796 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
797 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
800 assert_action_a5(ch
);
806 switch (ch
->ifjoin_state
) {
807 case PIM_IFJOIN_NOINFO
:
808 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
810 if (pim_macro_chisin_oiflist(ch
)) {
811 pim_upstream_inherited_olist(ch
->upstream
);
812 pim_forward_start(ch
);
815 * If we are going to be a LHR, we need to note it
817 if (ch
->upstream
->parent
818 && (ch
->upstream
->parent
->flags
819 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
820 && !(ch
->upstream
->flags
821 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
822 pim_upstream_ref(ch
->upstream
,
823 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
824 __PRETTY_FUNCTION__
);
825 pim_upstream_keep_alive_timer_start(
826 ch
->upstream
, qpim_keep_alive_time
);
829 case PIM_IFJOIN_JOIN
:
830 zassert(!ch
->t_ifjoin_prune_pending_timer
);
833 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
835 previously received join message with holdtime=0xFFFF.
837 if (ch
->t_ifjoin_expiry_timer
) {
838 unsigned long remain
= thread_timer_remain_second(
839 ch
->t_ifjoin_expiry_timer
);
840 if (remain
> holdtime
) {
842 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
845 Transitions from Join State
847 The (S,G) downstream state machine on
848 interface I remains in
849 Join state, and the Expiry Timer (ET) is
851 maximum of its current value and the HoldTime
853 triggering Join/Prune message.
855 Conclusion: Do not change the ET if the
857 higher than the received join holdtime.
862 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
864 case PIM_IFJOIN_PRUNE
:
865 if (source_flags
& PIM_ENCODE_RPT_BIT
)
866 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
869 case PIM_IFJOIN_PRUNE_PENDING
:
870 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
871 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
872 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
873 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
876 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
879 case PIM_IFJOIN_PRUNE_TMP
:
881 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
885 if (holdtime
!= 0xFFFF) {
886 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
887 &ch
->t_ifjoin_expiry_timer
);
891 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
892 struct prefix_sg
*sg
, uint8_t source_flags
,
895 struct pim_ifchannel
*ch
;
896 struct pim_interface
*pim_ifp
;
897 int jp_override_interval_msec
;
899 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
904 ch
= pim_ifchannel_find(ifp
, sg
);
905 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
908 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
909 __PRETTY_FUNCTION__
, ifp
->name
,
910 pim_str_sg_dump(sg
), source_flags
);
914 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
920 switch (ch
->ifjoin_state
) {
921 case PIM_IFJOIN_NOINFO
:
922 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
923 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
924 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
926 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
927 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
928 jp_override_interval_msec
=
929 pim_if_jp_override_interval_msec(ifp
);
931 jp_override_interval_msec
=
932 0; /* schedule to expire immediately */
933 /* If we called ifjoin_prune() directly instead, care
935 be taken not to use "ch" afterwards since it would be
938 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
939 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
940 thread_add_timer_msec(
941 master
, on_ifjoin_prune_pending_timer
, ch
,
942 jp_override_interval_msec
,
943 &ch
->t_ifjoin_prune_pending_timer
);
944 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
945 holdtime
, &ch
->t_ifjoin_expiry_timer
);
946 pim_upstream_update_join_desired(ch
->upstream
);
949 case PIM_IFJOIN_PRUNE_PENDING
:
952 case PIM_IFJOIN_JOIN
:
953 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
955 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
956 PIM_IFJOIN_PRUNE_PENDING
);
958 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
959 jp_override_interval_msec
=
960 pim_if_jp_override_interval_msec(ifp
);
962 jp_override_interval_msec
=
963 0; /* schedule to expire immediately */
964 /* If we called ifjoin_prune() directly instead, care should
965 be taken not to use "ch" afterwards since it would be
967 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
968 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
969 jp_override_interval_msec
,
970 &ch
->t_ifjoin_prune_pending_timer
);
972 case PIM_IFJOIN_PRUNE
:
973 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
974 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
975 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
976 holdtime
, &ch
->t_ifjoin_expiry_timer
);
979 case PIM_IFJOIN_PRUNE_TMP
:
980 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
981 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
982 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
983 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
984 holdtime
, &ch
->t_ifjoin_expiry_timer
);
987 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
988 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
989 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
990 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
991 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
992 holdtime
, &ch
->t_ifjoin_expiry_timer
);
998 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
999 struct prefix_sg
*sg
)
1001 struct pim_ifchannel
*ch
, *starch
;
1002 struct pim_interface
*pim_ifp
;
1004 /* PIM enabled on interface? */
1005 pim_ifp
= ifp
->info
;
1008 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1011 /* skip (*,G) ch creation if G is of type SSM */
1012 if (sg
->src
.s_addr
== INADDR_ANY
) {
1013 if (pim_is_grp_ssm(sg
->grp
)) {
1014 if (PIM_DEBUG_PIM_EVENTS
)
1016 "%s: local membership (S,G)=%s ignored as group is SSM",
1017 __PRETTY_FUNCTION__
,
1018 pim_str_sg_dump(sg
));
1023 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1028 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1030 if (sg
->src
.s_addr
== INADDR_ANY
) {
1031 struct pim_upstream
*up
= pim_upstream_find(sg
);
1032 struct pim_upstream
*child
;
1033 struct listnode
*up_node
;
1037 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1038 if (PIM_DEBUG_EVENTS
)
1039 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1040 __FILE__
, __PRETTY_FUNCTION__
,
1041 child
->sg_str
, ifp
->name
,
1044 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1045 if (pim_upstream_evaluate_join_desired_interface(
1046 child
, ch
, starch
)) {
1047 pim_channel_add_oif(child
->channel_oil
, ifp
,
1048 PIM_OIF_FLAG_PROTO_STAR
);
1049 pim_upstream_switch(child
, PIM_UPSTREAM_JOINED
);
1053 if (pimg
->spt
.switchover
== PIM_SPT_INFINITY
) {
1054 if (pimg
->spt
.plist
) {
1055 struct prefix_list
*plist
= prefix_list_lookup(
1056 AFI_IP
, pimg
->spt
.plist
);
1059 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1060 g
.u
.prefix4
= up
->sg
.grp
;
1062 if (prefix_list_apply(plist
, &g
)
1064 pim_channel_add_oif(
1065 up
->channel_oil
, pim_regiface
,
1066 PIM_OIF_FLAG_PROTO_IGMP
);
1070 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1071 PIM_OIF_FLAG_PROTO_IGMP
);
1077 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1078 struct prefix_sg
*sg
)
1080 struct pim_ifchannel
*starch
, *ch
, *orig
;
1081 struct pim_interface
*pim_ifp
;
1083 /* PIM enabled on interface? */
1084 pim_ifp
= ifp
->info
;
1087 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1090 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1094 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1096 if (sg
->src
.s_addr
== INADDR_ANY
) {
1097 struct pim_upstream
*up
= pim_upstream_find(sg
);
1098 struct pim_upstream
*child
;
1099 struct listnode
*up_node
, *up_nnode
;
1103 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1104 struct channel_oil
*c_oil
= child
->channel_oil
;
1105 struct pim_ifchannel
*chchannel
=
1106 pim_ifchannel_find(ifp
, &child
->sg
);
1107 struct pim_interface
*pim_ifp
= ifp
->info
;
1109 if (PIM_DEBUG_EVENTS
)
1110 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1111 __FILE__
, __PRETTY_FUNCTION__
,
1112 up
->sg_str
, ifp
->name
,
1115 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1117 && !pim_upstream_evaluate_join_desired_interface(
1119 pim_channel_del_oif(c_oil
, ifp
,
1120 PIM_OIF_FLAG_PROTO_STAR
);
1123 * If the S,G has no if channel and the c_oil still
1124 * has output here then the *,G was supplying the
1126 * if channel. So remove it.
1128 if (!chchannel
&& c_oil
1129 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1130 pim_channel_del_oif(c_oil
, ifp
,
1131 PIM_OIF_FLAG_PROTO_STAR
);
1133 /* Child node removal/ref count-- will happen as part of
1134 * parent' delete_no_info */
1137 delete_on_noinfo(orig
);
1140 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1142 int old_couldassert
=
1143 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1144 int new_couldassert
=
1145 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1147 if (new_couldassert
== old_couldassert
)
1150 if (PIM_DEBUG_PIM_EVENTS
) {
1151 char src_str
[INET_ADDRSTRLEN
];
1152 char grp_str
[INET_ADDRSTRLEN
];
1153 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1154 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1155 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1156 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1157 ch
->interface
->name
, old_couldassert
,
1161 if (new_couldassert
) {
1162 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1163 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1165 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1166 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1168 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1169 assert_action_a4(ch
);
1173 pim_ifchannel_update_my_assert_metric(ch
);
1177 my_assert_metric may be affected by:
1180 pim_ifp->primary_address
1181 rpf->source_nexthop.mrib_metric_preference;
1182 rpf->source_nexthop.mrib_route_metric;
1184 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1186 struct pim_assert_metric my_metric_new
=
1187 pim_macro_ch_my_assert_metric_eval(ch
);
1189 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1192 if (PIM_DEBUG_PIM_EVENTS
) {
1193 char src_str
[INET_ADDRSTRLEN
];
1194 char grp_str
[INET_ADDRSTRLEN
];
1195 char old_addr_str
[INET_ADDRSTRLEN
];
1196 char new_addr_str
[INET_ADDRSTRLEN
];
1197 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1198 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1199 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1200 old_addr_str
, sizeof(old_addr_str
));
1201 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1202 new_addr_str
, sizeof(new_addr_str
));
1204 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1205 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1206 ch
->interface
->name
,
1207 ch
->ifassert_my_metric
.rpt_bit_flag
,
1208 ch
->ifassert_my_metric
.metric_preference
,
1209 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1210 my_metric_new
.rpt_bit_flag
,
1211 my_metric_new
.metric_preference
,
1212 my_metric_new
.route_metric
, new_addr_str
);
1215 ch
->ifassert_my_metric
= my_metric_new
;
1217 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1218 &ch
->ifassert_winner_metric
)) {
1219 assert_action_a5(ch
);
1223 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1225 int old_atd
= PIM_FORCE_BOOLEAN(
1226 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1228 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1230 if (new_atd
== old_atd
)
1233 if (PIM_DEBUG_PIM_EVENTS
) {
1234 char src_str
[INET_ADDRSTRLEN
];
1235 char grp_str
[INET_ADDRSTRLEN
];
1236 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1237 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1239 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1240 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1241 ch
->interface
->name
, old_atd
, new_atd
);
1245 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1246 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1248 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1249 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1251 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1252 assert_action_a5(ch
);
1258 * If we have a new pim interface, check to
1259 * see if any of the pre-existing channels have
1260 * their upstream out that way and turn on forwarding
1261 * for that ifchannel then.
1263 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1265 struct listnode
*ifnode
;
1266 struct interface
*ifp
;
1267 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1269 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT
), ifnode
, ifp
)) {
1270 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1271 struct listnode
*ch_node
;
1272 struct pim_ifchannel
*ch
;
1277 if (new_pim_ifp
== loop_pim_ifp
)
1280 for (ALL_LIST_ELEMENTS_RO(loop_pim_ifp
->pim_ifchannel_list
,
1282 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1283 struct pim_upstream
*up
= ch
->upstream
;
1284 if ((!up
->channel_oil
)
1285 && (up
->rpf
.source_nexthop
1286 .interface
== new_ifp
))
1287 pim_forward_start(ch
);
1294 * Downstream per-interface (S,G,rpt) state machine
1295 * states that we need to move (S,G,rpt) items
1296 * into different states at the start of the
1297 * reception of a *,G join as well, when
1298 * we get End of Message
1300 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1301 uint8_t source_flags
, uint8_t join
,
1302 uint8_t starg_alone
)
1304 struct pim_ifchannel
*child
;
1305 struct listnode
*ch_node
;
1307 if (PIM_DEBUG_PIM_TRACE
)
1309 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1310 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1311 ch
->sg_str
, eom
, join
);
1315 for (ALL_LIST_ELEMENTS_RO(ch
->sources
, ch_node
, child
)) {
1316 /* Only *,G Join received and no (SG-RPT) prune.
1317 eom = 1, only (W,G) join_alone is true, WC and RPT are set.
1318 Scan all S,G associated to G and if any SG-RPT
1319 remove the SG-RPT flag.
1321 if (eom
&& starg_alone
&& (source_flags
& PIM_RPT_BIT_MASK
)
1322 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
1323 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
1324 struct pim_upstream
*up
= child
->upstream
;
1326 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1328 if (PIM_DEBUG_TRACE
)
1330 "%s: SGRpt flag is cleared, add inherit oif to up %s",
1331 __PRETTY_FUNCTION__
,
1333 pim_channel_add_oif(
1334 up
->channel_oil
, ch
->interface
,
1335 PIM_OIF_FLAG_PROTO_STAR
);
1336 pim_ifchannel_ifjoin_switch(
1337 __PRETTY_FUNCTION__
, child
,
1343 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1346 switch (child
->ifjoin_state
) {
1347 case PIM_IFJOIN_NOINFO
:
1348 case PIM_IFJOIN_JOIN
:
1350 case PIM_IFJOIN_PRUNE
:
1352 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1354 case PIM_IFJOIN_PRUNE_PENDING
:
1356 child
->ifjoin_state
=
1357 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1359 case PIM_IFJOIN_PRUNE_TMP
:
1360 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1362 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1368 unsigned int pim_ifchannel_hash_key(void *arg
)
1370 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1372 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);
1375 int pim_ifchannel_equal(const void *arg1
, const void *arg2
)
1377 const struct pim_ifchannel
*ch1
= (const struct pim_ifchannel
*)arg1
;
1378 const struct pim_ifchannel
*ch2
= (const struct pim_ifchannel
*)arg2
;
1380 if ((ch1
->sg
.grp
.s_addr
== ch2
->sg
.grp
.s_addr
)
1381 && (ch1
->sg
.src
.s_addr
== ch2
->sg
.src
.s_addr
))