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
&& (ch
->upstream
->parent
->flags
818 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
819 && !(ch
->upstream
->flags
820 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
821 pim_upstream_ref(ch
->upstream
,
822 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
823 __PRETTY_FUNCTION__
);
824 pim_upstream_keep_alive_timer_start(
825 ch
->upstream
, qpim_keep_alive_time
);
828 case PIM_IFJOIN_JOIN
:
829 zassert(!ch
->t_ifjoin_prune_pending_timer
);
832 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
834 previously received join message with holdtime=0xFFFF.
836 if (ch
->t_ifjoin_expiry_timer
) {
837 unsigned long remain
= thread_timer_remain_second(
838 ch
->t_ifjoin_expiry_timer
);
839 if (remain
> holdtime
) {
841 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
844 Transitions from Join State
846 The (S,G) downstream state machine on
847 interface I remains in
848 Join state, and the Expiry Timer (ET) is
850 maximum of its current value and the HoldTime
852 triggering Join/Prune message.
854 Conclusion: Do not change the ET if the
856 higher than the received join holdtime.
861 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
863 case PIM_IFJOIN_PRUNE
:
864 if (source_flags
& PIM_ENCODE_RPT_BIT
)
865 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
868 case PIM_IFJOIN_PRUNE_PENDING
:
869 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
870 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
871 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
872 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
875 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
878 case PIM_IFJOIN_PRUNE_TMP
:
880 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
884 if (holdtime
!= 0xFFFF) {
885 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
, holdtime
,
886 &ch
->t_ifjoin_expiry_timer
);
890 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
891 struct prefix_sg
*sg
, uint8_t source_flags
,
894 struct pim_ifchannel
*ch
;
895 struct pim_interface
*pim_ifp
;
896 int jp_override_interval_msec
;
898 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
903 ch
= pim_ifchannel_find(ifp
, sg
);
904 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
907 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
908 __PRETTY_FUNCTION__
, ifp
->name
,
909 pim_str_sg_dump(sg
), source_flags
);
913 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
919 switch (ch
->ifjoin_state
) {
920 case PIM_IFJOIN_NOINFO
:
921 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
922 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
923 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
925 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
926 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
927 jp_override_interval_msec
=
928 pim_if_jp_override_interval_msec(ifp
);
930 jp_override_interval_msec
=
931 0; /* schedule to expire immediately */
932 /* If we called ifjoin_prune() directly instead, care
934 be taken not to use "ch" afterwards since it would be
937 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
938 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
939 thread_add_timer_msec(
940 master
, on_ifjoin_prune_pending_timer
, ch
,
941 jp_override_interval_msec
,
942 &ch
->t_ifjoin_prune_pending_timer
);
943 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
944 holdtime
, &ch
->t_ifjoin_expiry_timer
);
945 pim_upstream_update_join_desired(ch
->upstream
);
948 case PIM_IFJOIN_PRUNE_PENDING
:
951 case PIM_IFJOIN_JOIN
:
952 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
954 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
955 PIM_IFJOIN_PRUNE_PENDING
);
957 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
958 jp_override_interval_msec
=
959 pim_if_jp_override_interval_msec(ifp
);
961 jp_override_interval_msec
=
962 0; /* schedule to expire immediately */
963 /* If we called ifjoin_prune() directly instead, care should
964 be taken not to use "ch" afterwards since it would be
966 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
967 thread_add_timer_msec(master
, on_ifjoin_prune_pending_timer
, ch
,
968 jp_override_interval_msec
,
969 &ch
->t_ifjoin_prune_pending_timer
);
971 case PIM_IFJOIN_PRUNE
:
972 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
973 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
974 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
975 holdtime
, &ch
->t_ifjoin_expiry_timer
);
978 case PIM_IFJOIN_PRUNE_TMP
:
979 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
980 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
981 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
982 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
983 holdtime
, &ch
->t_ifjoin_expiry_timer
);
986 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
987 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
988 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
989 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
990 thread_add_timer(master
, on_ifjoin_expiry_timer
, ch
,
991 holdtime
, &ch
->t_ifjoin_expiry_timer
);
997 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
998 struct prefix_sg
*sg
)
1000 struct pim_ifchannel
*ch
, *starch
;
1001 struct pim_interface
*pim_ifp
;
1003 /* PIM enabled on interface? */
1004 pim_ifp
= ifp
->info
;
1007 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1010 /* skip (*,G) ch creation if G is of type SSM */
1011 if (sg
->src
.s_addr
== INADDR_ANY
) {
1012 if (pim_is_grp_ssm(sg
->grp
)) {
1013 if (PIM_DEBUG_PIM_EVENTS
)
1015 "%s: local membership (S,G)=%s ignored as group is SSM",
1016 __PRETTY_FUNCTION__
,
1017 pim_str_sg_dump(sg
));
1022 ch
= pim_ifchannel_add(ifp
, sg
, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1027 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1029 if (sg
->src
.s_addr
== INADDR_ANY
) {
1030 struct pim_upstream
*up
= pim_upstream_find(sg
);
1031 struct pim_upstream
*child
;
1032 struct listnode
*up_node
;
1036 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1037 if (PIM_DEBUG_EVENTS
)
1038 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1039 __FILE__
, __PRETTY_FUNCTION__
,
1040 child
->sg_str
, ifp
->name
,
1043 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1044 if (pim_upstream_evaluate_join_desired_interface(
1045 child
, ch
, starch
)) {
1046 pim_channel_add_oif(child
->channel_oil
, ifp
,
1047 PIM_OIF_FLAG_PROTO_STAR
);
1048 pim_upstream_switch(child
, PIM_UPSTREAM_JOINED
);
1052 if (pimg
->spt
.switchover
== PIM_SPT_INFINITY
) {
1053 if (pimg
->spt
.plist
) {
1054 struct prefix_list
*plist
= prefix_list_lookup(
1055 AFI_IP
, pimg
->spt
.plist
);
1058 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1059 g
.u
.prefix4
= up
->sg
.grp
;
1061 if (prefix_list_apply(plist
, &g
)
1063 pim_channel_add_oif(
1064 up
->channel_oil
, pim_regiface
,
1065 PIM_OIF_FLAG_PROTO_IGMP
);
1069 pim_channel_add_oif(up
->channel_oil
, pim_regiface
,
1070 PIM_OIF_FLAG_PROTO_IGMP
);
1076 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1077 struct prefix_sg
*sg
)
1079 struct pim_ifchannel
*starch
, *ch
, *orig
;
1080 struct pim_interface
*pim_ifp
;
1082 /* PIM enabled on interface? */
1083 pim_ifp
= ifp
->info
;
1086 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1089 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1093 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1095 if (sg
->src
.s_addr
== INADDR_ANY
) {
1096 struct pim_upstream
*up
= pim_upstream_find(sg
);
1097 struct pim_upstream
*child
;
1098 struct listnode
*up_node
, *up_nnode
;
1102 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1103 struct channel_oil
*c_oil
= child
->channel_oil
;
1104 struct pim_ifchannel
*chchannel
=
1105 pim_ifchannel_find(ifp
, &child
->sg
);
1106 struct pim_interface
*pim_ifp
= ifp
->info
;
1108 if (PIM_DEBUG_EVENTS
)
1109 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1110 __FILE__
, __PRETTY_FUNCTION__
,
1111 up
->sg_str
, ifp
->name
,
1114 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1116 && !pim_upstream_evaluate_join_desired_interface(
1118 pim_channel_del_oif(c_oil
, ifp
,
1119 PIM_OIF_FLAG_PROTO_STAR
);
1122 * If the S,G has no if channel and the c_oil still
1123 * has output here then the *,G was supplying the
1125 * if channel. So remove it.
1127 if (!chchannel
&& c_oil
1128 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1129 pim_channel_del_oif(c_oil
, ifp
,
1130 PIM_OIF_FLAG_PROTO_STAR
);
1132 /* Child node removal/ref count-- will happen as part of
1133 * parent' delete_no_info */
1136 delete_on_noinfo(orig
);
1139 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1141 int old_couldassert
=
1142 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1143 int new_couldassert
=
1144 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1146 if (new_couldassert
== old_couldassert
)
1149 if (PIM_DEBUG_PIM_EVENTS
) {
1150 char src_str
[INET_ADDRSTRLEN
];
1151 char grp_str
[INET_ADDRSTRLEN
];
1152 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1153 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1154 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1155 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1156 ch
->interface
->name
, old_couldassert
,
1160 if (new_couldassert
) {
1161 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1162 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1164 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1165 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1167 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1168 assert_action_a4(ch
);
1172 pim_ifchannel_update_my_assert_metric(ch
);
1176 my_assert_metric may be affected by:
1179 pim_ifp->primary_address
1180 rpf->source_nexthop.mrib_metric_preference;
1181 rpf->source_nexthop.mrib_route_metric;
1183 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1185 struct pim_assert_metric my_metric_new
=
1186 pim_macro_ch_my_assert_metric_eval(ch
);
1188 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1191 if (PIM_DEBUG_PIM_EVENTS
) {
1192 char src_str
[INET_ADDRSTRLEN
];
1193 char grp_str
[INET_ADDRSTRLEN
];
1194 char old_addr_str
[INET_ADDRSTRLEN
];
1195 char new_addr_str
[INET_ADDRSTRLEN
];
1196 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1197 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1198 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1199 old_addr_str
, sizeof(old_addr_str
));
1200 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1201 new_addr_str
, sizeof(new_addr_str
));
1203 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1204 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1205 ch
->interface
->name
,
1206 ch
->ifassert_my_metric
.rpt_bit_flag
,
1207 ch
->ifassert_my_metric
.metric_preference
,
1208 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1209 my_metric_new
.rpt_bit_flag
,
1210 my_metric_new
.metric_preference
,
1211 my_metric_new
.route_metric
, new_addr_str
);
1214 ch
->ifassert_my_metric
= my_metric_new
;
1216 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1217 &ch
->ifassert_winner_metric
)) {
1218 assert_action_a5(ch
);
1222 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1224 int old_atd
= PIM_FORCE_BOOLEAN(
1225 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1227 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1229 if (new_atd
== old_atd
)
1232 if (PIM_DEBUG_PIM_EVENTS
) {
1233 char src_str
[INET_ADDRSTRLEN
];
1234 char grp_str
[INET_ADDRSTRLEN
];
1235 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1236 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1238 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1239 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1240 ch
->interface
->name
, old_atd
, new_atd
);
1244 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1245 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1247 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1248 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1250 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1251 assert_action_a5(ch
);
1257 * If we have a new pim interface, check to
1258 * see if any of the pre-existing channels have
1259 * their upstream out that way and turn on forwarding
1260 * for that ifchannel then.
1262 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1264 struct listnode
*ifnode
;
1265 struct interface
*ifp
;
1266 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1268 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT
), ifnode
, ifp
)) {
1269 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1270 struct listnode
*ch_node
;
1271 struct pim_ifchannel
*ch
;
1276 if (new_pim_ifp
== loop_pim_ifp
)
1279 for (ALL_LIST_ELEMENTS_RO(loop_pim_ifp
->pim_ifchannel_list
,
1281 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1282 struct pim_upstream
*up
= ch
->upstream
;
1283 if ((!up
->channel_oil
)
1284 && (up
->rpf
.source_nexthop
1285 .interface
== new_ifp
))
1286 pim_forward_start(ch
);
1293 * Downstream per-interface (S,G,rpt) state machine
1294 * states that we need to move (S,G,rpt) items
1295 * into different states at the start of the
1296 * reception of a *,G join as well, when
1297 * we get End of Message
1299 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1300 uint8_t source_flags
, uint8_t join
,
1301 uint8_t starg_alone
)
1303 struct pim_ifchannel
*child
;
1304 struct listnode
*ch_node
;
1306 if (PIM_DEBUG_PIM_TRACE
)
1308 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1309 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1310 ch
->sg_str
, eom
, join
);
1314 for (ALL_LIST_ELEMENTS_RO(ch
->sources
, ch_node
, child
)) {
1315 /* Only *,G Join received and no (SG-RPT) prune.
1316 eom = 1, only (W,G) join_alone is true, WC and RPT are set.
1317 Scan all S,G associated to G and if any SG-RPT
1318 remove the SG-RPT flag.
1320 if (eom
&& starg_alone
&& (source_flags
& PIM_RPT_BIT_MASK
)
1321 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
1322 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
1323 struct pim_upstream
*up
= child
->upstream
;
1325 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1327 if (PIM_DEBUG_TRACE
)
1329 "%s: SGRpt flag is cleared, add inherit oif to up %s",
1330 __PRETTY_FUNCTION__
,
1332 pim_channel_add_oif(
1333 up
->channel_oil
, ch
->interface
,
1334 PIM_OIF_FLAG_PROTO_STAR
);
1335 pim_ifchannel_ifjoin_switch(
1336 __PRETTY_FUNCTION__
, child
,
1342 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1345 switch (child
->ifjoin_state
) {
1346 case PIM_IFJOIN_NOINFO
:
1347 case PIM_IFJOIN_JOIN
:
1349 case PIM_IFJOIN_PRUNE
:
1351 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1353 case PIM_IFJOIN_PRUNE_PENDING
:
1355 child
->ifjoin_state
=
1356 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1358 case PIM_IFJOIN_PRUNE_TMP
:
1359 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1361 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1367 unsigned int pim_ifchannel_hash_key(void *arg
)
1369 struct pim_ifchannel
*ch
= (struct pim_ifchannel
*)arg
;
1371 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);
1374 int pim_ifchannel_equal(const void *arg1
, const void *arg2
)
1376 const struct pim_ifchannel
*ch1
= (const struct pim_ifchannel
*)arg1
;
1377 const struct pim_ifchannel
*ch2
= (const struct pim_ifchannel
*)arg2
;
1379 if ((ch1
->sg
.grp
.s_addr
== ch2
->sg
.grp
.s_addr
)
1380 && (ch1
->sg
.src
.s_addr
== ch2
->sg
.src
.s_addr
))