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"
47 RB_GENERATE(pim_ifchannel_rb
, pim_ifchannel
, pim_ifp_rb
, pim_ifchannel_compare
);
49 int pim_ifchannel_compare(const struct pim_ifchannel
*ch1
,
50 const struct pim_ifchannel
*ch2
)
52 struct pim_interface
*pim_ifp1
;
53 struct pim_interface
*pim_ifp2
;
55 pim_ifp1
= ch1
->interface
->info
;
56 pim_ifp2
= ch2
->interface
->info
;
58 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
61 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
64 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
67 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
70 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
73 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
80 * A (*,G) or a (*,*) is going away
81 * remove the parent pointer from
82 * those pointing at us
84 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
86 struct pim_ifchannel
*child
;
91 while (!list_isempty(ch
->sources
)) {
92 child
= listnode_head(ch
->sources
);
94 listnode_delete(ch
->sources
, child
);
99 * A (*,G) or a (*,*) is being created
100 * find all the children that would point
103 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
105 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
106 struct pim_ifchannel
*child
;
108 // Basic Sanity that we are not being silly
109 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
)
110 && (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
113 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
)
114 && (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
117 RB_FOREACH (child
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
) {
118 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
)
119 && (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
)
122 listnode_add_sort(ch
->sources
, child
);
127 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
129 struct pim_interface
*pim_ifp
;
131 pim_ifp
= ch
->interface
->info
;
133 if (ch
->upstream
->channel_oil
) {
134 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
135 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
136 mask
|= PIM_OIF_FLAG_PROTO_IGMP
;
139 * A S,G RPT channel can have an empty oil, we also
140 * need to take into account the fact that a ifchannel
141 * might have been suppressing a *,G ifchannel from
142 * being inherited. So let's figure out what
143 * needs to be done here
145 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
146 pim_upstream_evaluate_join_desired_interface(
147 ch
->upstream
, ch
, ch
->parent
))
148 pim_channel_add_oif(ch
->upstream
->channel_oil
,
150 PIM_OIF_FLAG_PROTO_STAR
);
152 pim_channel_del_oif(ch
->upstream
->channel_oil
,
153 ch
->interface
, mask
);
155 * Do we have any S,G's that are inheriting?
156 * Nuke from on high too.
158 if (ch
->upstream
->sources
) {
159 struct pim_upstream
*child
;
160 struct listnode
*up_node
;
162 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
164 pim_channel_del_oif(child
->channel_oil
,
166 PIM_OIF_FLAG_PROTO_STAR
);
171 * When this channel is removed
172 * we need to find all our children
173 * and make sure our pointers are fixed
175 pim_ifchannel_remove_children(ch
);
178 list_delete(&ch
->sources
);
180 listnode_delete(ch
->upstream
->ifchannels
, ch
);
182 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
183 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
186 /* upstream is common across ifchannels, check if upstream's
187 ifchannel list is empty before deleting upstream_del
188 ref count will take care of it.
190 if (ch
->upstream
->ref_count
> 0)
191 pim_upstream_del(pim_ifp
->pim
, ch
->upstream
,
192 __PRETTY_FUNCTION__
);
195 zlog_warn("%s: Avoiding deletion of upstream with ref_count %d "
196 "from ifchannel(%s): %s", __PRETTY_FUNCTION__
,
197 ch
->upstream
->ref_count
, ch
->interface
->name
,
202 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
203 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
204 THREAD_OFF(ch
->t_ifassert_timer
);
207 listnode_delete(ch
->parent
->sources
, ch
);
211 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
213 if (PIM_DEBUG_PIM_TRACE
)
214 zlog_debug("%s: ifchannel entry %s is deleted ",
215 __PRETTY_FUNCTION__
, ch
->sg_str
);
217 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
220 void pim_ifchannel_delete_all(struct interface
*ifp
)
222 struct pim_interface
*pim_ifp
;
223 struct pim_ifchannel
*ch
;
229 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
230 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
232 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
,
233 ch
, PIM_IFJOIN_NOINFO
);
234 pim_ifchannel_delete(ch
);
238 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
240 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
241 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
242 && ch
->t_ifjoin_expiry_timer
== NULL
)
243 pim_ifchannel_delete(ch
);
246 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
247 enum pim_ifjoin_state new_state
)
249 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
250 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
252 if (PIM_DEBUG_PIM_EVENTS
)
254 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
255 ch
->interface
->name
, ch
->sg_str
,
256 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
257 pim_ifchannel_ifjoin_name(new_state
, 0));
260 if (old_state
== new_state
) {
261 if (PIM_DEBUG_PIM_EVENTS
) {
263 "%s calledby %s: non-transition on state %d (%s)",
264 __PRETTY_FUNCTION__
, caller
, new_state
,
265 pim_ifchannel_ifjoin_name(new_state
, 0));
270 ch
->ifjoin_state
= new_state
;
272 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
273 struct pim_upstream
*up
= ch
->upstream
;
274 struct pim_upstream
*child
;
275 struct listnode
*up_node
;
278 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
279 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
281 struct channel_oil
*c_oil
=
284 if (PIM_DEBUG_PIM_TRACE
)
286 "%s %s: Prune(S,G)=%s from %s",
294 if (!pim_upstream_evaluate_join_desired(
295 pim_ifp
->pim
, child
)) {
297 c_oil
, ch
->interface
,
298 PIM_OIF_FLAG_PROTO_STAR
);
299 pim_upstream_update_join_desired(
300 pim_ifp
->pim
, child
);
304 * If the S,G has no if channel and the
306 * has output here then the *,G was
307 * supplying the implied
308 * if channel. So remove it.
309 * I think this is dead code now. is it?
311 if (c_oil
->oil
.mfcc_ttls
312 [pim_ifp
->mroute_vif_index
])
314 c_oil
, ch
->interface
,
315 PIM_OIF_FLAG_PROTO_STAR
);
318 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
319 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
321 if (PIM_DEBUG_PIM_TRACE
)
323 "%s %s: Join(S,G)=%s from %s",
329 if (pim_upstream_evaluate_join_desired(
330 pim_ifp
->pim
, child
)) {
334 PIM_OIF_FLAG_PROTO_STAR
);
335 pim_upstream_update_join_desired(
336 pim_ifp
->pim
, child
);
342 /* Transition to/from NOINFO ? */
343 if ((old_state
== PIM_IFJOIN_NOINFO
)
344 || (new_state
== PIM_IFJOIN_NOINFO
)) {
346 if (PIM_DEBUG_PIM_EVENTS
) {
347 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
348 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
350 ch
->sg_str
, ch
->interface
->name
);
354 Record uptime of state transition to/from NOINFO
356 ch
->ifjoin_creation
= pim_time_monotonic_sec();
358 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
359 pim_ifchannel_update_could_assert(ch
);
360 pim_ifchannel_update_assert_tracking_desired(ch
);
364 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
367 switch (ifjoin_state
) {
368 case PIM_IFJOIN_NOINFO
:
369 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
374 case PIM_IFJOIN_JOIN
:
377 case PIM_IFJOIN_PRUNE
:
378 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
383 case PIM_IFJOIN_PRUNE_PENDING
:
384 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
389 case PIM_IFJOIN_PRUNE_TMP
:
390 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
395 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
396 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
403 return "ifjoin_bad_state";
406 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
408 switch (ifassert_state
) {
409 case PIM_IFASSERT_NOINFO
:
411 case PIM_IFASSERT_I_AM_WINNER
:
413 case PIM_IFASSERT_I_AM_LOSER
:
417 return "ifassert_bad_state";
421 RFC 4601: 4.6.5. Assert State Macros
423 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
424 defaults to Infinity when in the NoInfo state.
426 void reset_ifassert_state(struct pim_ifchannel
*ch
)
428 struct in_addr any
= {.s_addr
= INADDR_ANY
};
430 THREAD_OFF(ch
->t_ifassert_timer
);
432 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
433 router
->infinite_assert_metric
);
436 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
437 struct prefix_sg
*sg
)
439 struct pim_interface
*pim_ifp
;
440 struct pim_ifchannel
*ch
;
441 struct pim_ifchannel lookup
;
446 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
447 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
452 lookup
.interface
= ifp
;
453 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
458 static void ifmembership_set(struct pim_ifchannel
*ch
,
459 enum pim_ifmembership membership
)
461 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
463 if (ch
->local_ifmembership
== membership
)
466 if (PIM_DEBUG_PIM_EVENTS
) {
467 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
468 __PRETTY_FUNCTION__
, ch
->sg_str
,
469 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
471 ch
->interface
->name
);
474 ch
->local_ifmembership
= membership
;
476 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
477 pim_ifchannel_update_could_assert(ch
);
478 pim_ifchannel_update_assert_tracking_desired(ch
);
482 void pim_ifchannel_membership_clear(struct interface
*ifp
)
484 struct pim_interface
*pim_ifp
;
485 struct pim_ifchannel
*ch
;
490 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
491 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
494 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
496 struct pim_interface
*pim_ifp
;
497 struct pim_ifchannel
*ch
, *ch_tmp
;
502 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
503 delete_on_noinfo(ch
);
507 * For a given Interface, if we are given a S,G
508 * Find the *,G (If we have it).
509 * If we are passed a *,G, find the *,* ifchannel
512 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
514 struct prefix_sg parent_sg
= ch
->sg
;
515 struct pim_ifchannel
*parent
= NULL
;
518 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
519 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
520 parent_sg
.src
.s_addr
= INADDR_ANY
;
521 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
524 listnode_add(parent
->sources
, ch
);
531 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
532 struct prefix_sg
*sg
,
533 uint8_t source_flags
, int up_flags
)
535 struct pim_interface
*pim_ifp
;
536 struct pim_ifchannel
*ch
;
537 struct pim_upstream
*up
;
539 ch
= pim_ifchannel_find(ifp
, sg
);
545 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
548 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
549 && !(source_flags
& PIM_ENCODE_WC_BIT
))
550 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
554 pim_str_sg_set(sg
, ch
->sg_str
);
555 ch
->parent
= pim_ifchannel_find_parent(ch
);
556 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
557 ch
->sources
= list_new();
559 (int (*)(void *, void *))pim_ifchannel_compare
;
563 pim_ifchannel_find_new_children(ch
);
564 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
566 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
567 ch
->t_ifjoin_expiry_timer
= NULL
;
568 ch
->t_ifjoin_prune_pending_timer
= NULL
;
569 ch
->ifjoin_creation
= 0;
571 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
573 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
,
574 __PRETTY_FUNCTION__
, ch
);
578 listnode_add_sort(up
->ifchannels
, ch
);
580 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
581 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
583 ch
->ifassert_winner
.s_addr
= 0;
586 ch
->t_ifassert_timer
= NULL
;
587 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
588 reset_ifassert_state(ch
);
589 if (pim_macro_ch_could_assert_eval(ch
))
590 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
592 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
594 if (pim_macro_assert_tracking_desired_eval(ch
))
595 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
597 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
599 if (PIM_DEBUG_PIM_TRACE
)
600 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
606 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
608 pim_forward_stop(ch
, !ch_del
);
609 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
611 delete_on_noinfo(ch
);
614 static int on_ifjoin_expiry_timer(struct thread
*t
)
616 struct pim_ifchannel
*ch
;
621 zlog_debug("%s: ifchannel %s expiry timer", __PRETTY_FUNCTION__
,
624 ifjoin_to_noinfo(ch
, true);
625 /* ch may have been deleted */
630 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
632 struct pim_ifchannel
*ch
;
633 int send_prune_echo
; /* boolean */
634 struct interface
*ifp
;
635 struct pim_interface
*pim_ifp
;
641 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
642 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
643 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
645 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
648 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
649 /* Send PruneEcho(S,G) ? */
651 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
653 if (send_prune_echo
) {
656 rpf
.source_nexthop
.interface
= ifp
;
657 rpf
.rpf_addr
.u
.prefix4
=
658 pim_ifp
->primary_address
;
659 pim_jp_agg_single_upstream_send(
660 &rpf
, ch
->upstream
, 0);
663 ifjoin_to_noinfo(ch
, true);
665 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
666 * message on RP path upon prune timer expiry.
668 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
670 struct pim_upstream
*parent
=
671 ch
->upstream
->parent
;
673 pim_upstream_update_join_desired(pim_ifp
->pim
,
676 pim_jp_agg_single_upstream_send(&parent
->rpf
,
680 /* from here ch may have been deleted */
686 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
687 struct in_addr upstream
, struct prefix_sg
*sg
,
688 uint8_t source_flags
, int holdtime
)
690 struct pim_upstream
*up
;
691 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
693 /* Upstream (S,G) in Joined state ? */
694 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
697 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
700 /* Upstream (S,G) in Joined state */
702 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
703 /* RPF'(S,G) not found */
704 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
705 __PRETTY_FUNCTION__
, up
->sg_str
);
709 /* upstream directed to RPF'(S,G) ? */
710 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
711 char up_str
[INET_ADDRSTRLEN
];
712 char rpf_str
[PREFIX_STRLEN
];
713 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
714 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
717 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
718 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
719 rpf_str
, recv_ifp
->name
);
722 /* upstream directed to RPF'(S,G) */
725 /* Join(S,G) to RPF'(S,G) */
726 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
731 /* Prune to RPF'(S,G) */
733 if (source_flags
& PIM_RPT_BIT_MASK
) {
734 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
735 /* Prune(*,G) to RPF'(S,G) */
736 pim_upstream_join_timer_decrease_to_t_override(
741 /* Prune(S,G,rpt) to RPF'(S,G) */
742 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
747 /* Prune(S,G) to RPF'(S,G) */
748 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
751 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
752 struct in_addr upstream
, struct prefix_sg
*sg
,
753 uint8_t source_flags
, uint16_t holdtime
)
755 struct pim_interface
*recv_pim_ifp
;
756 int is_local
; /* boolean */
758 recv_pim_ifp
= recv_ifp
->info
;
759 zassert(recv_pim_ifp
);
761 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
766 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
767 char up_str
[INET_ADDRSTRLEN
];
768 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
769 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
770 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
771 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
775 * Since recv upstream addr was not directed to our primary
776 * address, check if we should react to it in any way.
778 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
781 return 1; /* non-local */
784 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
785 struct in_addr upstream
, struct prefix_sg
*sg
,
786 uint8_t source_flags
, uint16_t holdtime
)
788 struct pim_interface
*pim_ifp
;
789 struct pim_ifchannel
*ch
;
791 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
796 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
797 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
800 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
802 Transitions from "I am Assert Loser" State
804 Receive Join(S,G) on Interface I
806 We receive a Join(S,G) that has the Upstream Neighbor Address
807 field set to my primary IP address on interface I. The action is
808 to transition to NoInfo state, delete this (S,G) assert state
809 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
812 Notice: The nonlocal_upstream() test above ensures the upstream
813 address of the join message is our primary address.
815 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
816 char neigh_str
[INET_ADDRSTRLEN
];
817 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
819 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
820 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
823 assert_action_a5(ch
);
829 switch (ch
->ifjoin_state
) {
830 case PIM_IFJOIN_NOINFO
:
831 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
833 if (pim_macro_chisin_oiflist(ch
)) {
834 pim_upstream_inherited_olist(pim_ifp
->pim
,
836 pim_forward_start(ch
);
839 * If we are going to be a LHR, we need to note it
841 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
842 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
843 && !(ch
->upstream
->flags
844 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
845 pim_upstream_ref(ch
->upstream
,
846 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
847 __PRETTY_FUNCTION__
);
848 pim_upstream_keep_alive_timer_start(
849 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
852 case PIM_IFJOIN_JOIN
:
853 zassert(!ch
->t_ifjoin_prune_pending_timer
);
856 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
858 previously received join message with holdtime=0xFFFF.
860 if (ch
->t_ifjoin_expiry_timer
) {
861 unsigned long remain
= thread_timer_remain_second(
862 ch
->t_ifjoin_expiry_timer
);
863 if (remain
> holdtime
) {
865 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
868 Transitions from Join State
870 The (S,G) downstream state machine on
871 interface I remains in
872 Join state, and the Expiry Timer (ET) is
874 maximum of its current value and the HoldTime
876 triggering Join/Prune message.
878 Conclusion: Do not change the ET if the
880 higher than the received join holdtime.
885 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
887 case PIM_IFJOIN_PRUNE
:
888 if (source_flags
& PIM_ENCODE_RPT_BIT
)
889 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
893 * We have received a S,G join and we are in
894 * S,G RPT Prune state. Which means we need
895 * to transition to Join state and setup
896 * state as appropriate.
898 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
900 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
901 if (pim_upstream_evaluate_join_desired(pim_ifp
->pim
,
903 pim_channel_add_oif(ch
->upstream
->channel_oil
,
905 PIM_OIF_FLAG_PROTO_PIM
);
906 pim_upstream_update_join_desired(pim_ifp
->pim
,
911 case PIM_IFJOIN_PRUNE_PENDING
:
912 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
913 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
914 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
915 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
918 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
921 case PIM_IFJOIN_PRUNE_TMP
:
923 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
927 if (holdtime
!= 0xFFFF) {
928 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
929 holdtime
, &ch
->t_ifjoin_expiry_timer
);
933 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
934 struct prefix_sg
*sg
, uint8_t source_flags
,
937 struct pim_ifchannel
*ch
;
938 struct pim_interface
*pim_ifp
;
939 int jp_override_interval_msec
;
941 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
946 ch
= pim_ifchannel_find(ifp
, sg
);
947 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
950 "%s: Received prune with no relevant ifchannel %s%s state: %d",
951 __PRETTY_FUNCTION__
, ifp
->name
,
952 pim_str_sg_dump(sg
), source_flags
);
956 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
957 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
961 switch (ch
->ifjoin_state
) {
962 case PIM_IFJOIN_NOINFO
:
963 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
964 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
965 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
967 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
968 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
969 jp_override_interval_msec
=
970 pim_if_jp_override_interval_msec(ifp
);
972 jp_override_interval_msec
=
973 0; /* schedule to expire immediately */
974 /* If we called ifjoin_prune() directly instead, care
976 be taken not to use "ch" afterwards since it would be
979 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
980 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
981 thread_add_timer_msec(
982 router
->master
, on_ifjoin_prune_pending_timer
,
983 ch
, jp_override_interval_msec
,
984 &ch
->t_ifjoin_prune_pending_timer
);
985 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
987 &ch
->t_ifjoin_expiry_timer
);
988 pim_upstream_update_join_desired(pim_ifp
->pim
,
992 case PIM_IFJOIN_PRUNE_PENDING
:
995 case PIM_IFJOIN_JOIN
:
996 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
998 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
999 PIM_IFJOIN_PRUNE_PENDING
);
1001 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1002 jp_override_interval_msec
=
1003 pim_if_jp_override_interval_msec(ifp
);
1005 jp_override_interval_msec
=
1006 0; /* schedule to expire immediately */
1007 /* If we called ifjoin_prune() directly instead, care should
1008 be taken not to use "ch" afterwards since it would be
1010 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1011 thread_add_timer_msec(router
->master
,
1012 on_ifjoin_prune_pending_timer
, ch
,
1013 jp_override_interval_msec
,
1014 &ch
->t_ifjoin_prune_pending_timer
);
1016 case PIM_IFJOIN_PRUNE
:
1017 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1018 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1019 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1021 &ch
->t_ifjoin_expiry_timer
);
1024 case PIM_IFJOIN_PRUNE_TMP
:
1025 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1026 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1027 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1028 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1030 &ch
->t_ifjoin_expiry_timer
);
1033 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1034 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1035 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1036 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1037 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1039 &ch
->t_ifjoin_expiry_timer
);
1045 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1046 struct prefix_sg
*sg
)
1048 struct pim_ifchannel
*ch
, *starch
;
1049 struct pim_interface
*pim_ifp
;
1050 struct pim_instance
*pim
;
1052 /* PIM enabled on interface? */
1053 pim_ifp
= ifp
->info
;
1055 if (PIM_DEBUG_EVENTS
)
1056 zlog_debug("%s:%s Expected pim interface setup for %s",
1057 __PRETTY_FUNCTION__
,
1058 pim_str_sg_dump(sg
), ifp
->name
);
1062 if (!PIM_IF_TEST_PIM(pim_ifp
->options
)) {
1063 if (PIM_DEBUG_EVENTS
)
1064 zlog_debug("%s:%s PIM is not configured on this interface %s",
1065 __PRETTY_FUNCTION__
,
1066 pim_str_sg_dump(sg
), ifp
->name
);
1072 /* skip (*,G) ch creation if G is of type SSM */
1073 if (sg
->src
.s_addr
== INADDR_ANY
) {
1074 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1075 if (PIM_DEBUG_PIM_EVENTS
)
1077 "%s: local membership (S,G)=%s ignored as group is SSM",
1078 __PRETTY_FUNCTION__
,
1079 pim_str_sg_dump(sg
));
1084 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1086 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1088 if (sg
->src
.s_addr
== INADDR_ANY
) {
1089 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1090 struct pim_upstream
*child
;
1091 struct listnode
*up_node
;
1095 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1096 if (PIM_DEBUG_EVENTS
)
1097 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1098 __FILE__
, __PRETTY_FUNCTION__
,
1099 child
->sg_str
, ifp
->name
,
1102 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1103 if (pim_upstream_evaluate_join_desired_interface(
1104 child
, ch
, starch
)) {
1105 pim_channel_add_oif(child
->channel_oil
, ifp
,
1106 PIM_OIF_FLAG_PROTO_STAR
);
1107 pim_upstream_switch(pim
, child
,
1108 PIM_UPSTREAM_JOINED
);
1112 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1113 if (pim
->spt
.plist
) {
1114 struct prefix_list
*plist
= prefix_list_lookup(
1115 AFI_IP
, pim
->spt
.plist
);
1118 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1119 g
.u
.prefix4
= up
->sg
.grp
;
1121 if (prefix_list_apply(plist
, &g
)
1123 pim_channel_add_oif(
1124 up
->channel_oil
, pim
->regiface
,
1125 PIM_OIF_FLAG_PROTO_IGMP
);
1129 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1130 PIM_OIF_FLAG_PROTO_IGMP
);
1136 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1137 struct prefix_sg
*sg
)
1139 struct pim_ifchannel
*starch
, *ch
, *orig
;
1140 struct pim_interface
*pim_ifp
;
1142 /* PIM enabled on interface? */
1143 pim_ifp
= ifp
->info
;
1146 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1149 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1152 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1154 if (sg
->src
.s_addr
== INADDR_ANY
) {
1155 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1156 struct pim_upstream
*child
;
1157 struct listnode
*up_node
, *up_nnode
;
1161 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1162 struct channel_oil
*c_oil
= child
->channel_oil
;
1163 struct pim_ifchannel
*chchannel
=
1164 pim_ifchannel_find(ifp
, &child
->sg
);
1166 pim_ifp
= ifp
->info
;
1168 if (PIM_DEBUG_EVENTS
)
1169 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1170 __FILE__
, __PRETTY_FUNCTION__
,
1171 up
->sg_str
, ifp
->name
,
1174 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1176 && !pim_upstream_evaluate_join_desired_interface(
1178 pim_channel_del_oif(c_oil
, ifp
,
1179 PIM_OIF_FLAG_PROTO_STAR
);
1182 * If the S,G has no if channel and the c_oil still
1183 * has output here then the *,G was supplying the
1185 * if channel. So remove it.
1187 if (!chchannel
&& c_oil
1188 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1189 pim_channel_del_oif(c_oil
, ifp
,
1190 PIM_OIF_FLAG_PROTO_STAR
);
1192 /* Child node removal/ref count-- will happen as part of
1193 * parent' delete_no_info */
1196 delete_on_noinfo(orig
);
1199 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1201 int old_couldassert
=
1202 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1203 int new_couldassert
=
1204 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1206 if (new_couldassert
== old_couldassert
)
1209 if (PIM_DEBUG_PIM_EVENTS
) {
1210 char src_str
[INET_ADDRSTRLEN
];
1211 char grp_str
[INET_ADDRSTRLEN
];
1212 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1213 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1214 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1215 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1216 ch
->interface
->name
, old_couldassert
,
1220 if (new_couldassert
) {
1221 /* CouldAssert(S,G,I) switched from false to true */
1222 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1224 /* CouldAssert(S,G,I) switched from true to false */
1225 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1227 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1228 assert_action_a4(ch
);
1232 pim_ifchannel_update_my_assert_metric(ch
);
1236 my_assert_metric may be affected by:
1239 pim_ifp->primary_address
1240 rpf->source_nexthop.mrib_metric_preference;
1241 rpf->source_nexthop.mrib_route_metric;
1243 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1245 struct pim_assert_metric my_metric_new
=
1246 pim_macro_ch_my_assert_metric_eval(ch
);
1248 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1251 if (PIM_DEBUG_PIM_EVENTS
) {
1252 char src_str
[INET_ADDRSTRLEN
];
1253 char grp_str
[INET_ADDRSTRLEN
];
1254 char old_addr_str
[INET_ADDRSTRLEN
];
1255 char new_addr_str
[INET_ADDRSTRLEN
];
1256 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1257 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1258 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1259 old_addr_str
, sizeof(old_addr_str
));
1260 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1261 new_addr_str
, sizeof(new_addr_str
));
1263 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1264 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1265 ch
->interface
->name
,
1266 ch
->ifassert_my_metric
.rpt_bit_flag
,
1267 ch
->ifassert_my_metric
.metric_preference
,
1268 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1269 my_metric_new
.rpt_bit_flag
,
1270 my_metric_new
.metric_preference
,
1271 my_metric_new
.route_metric
, new_addr_str
);
1274 ch
->ifassert_my_metric
= my_metric_new
;
1276 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1277 &ch
->ifassert_winner_metric
)) {
1278 assert_action_a5(ch
);
1282 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1284 int old_atd
= PIM_FORCE_BOOLEAN(
1285 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1287 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1289 if (new_atd
== old_atd
)
1292 if (PIM_DEBUG_PIM_EVENTS
) {
1293 char src_str
[INET_ADDRSTRLEN
];
1294 char grp_str
[INET_ADDRSTRLEN
];
1295 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1296 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1298 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1299 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1300 ch
->interface
->name
, old_atd
, new_atd
);
1304 /* AssertTrackingDesired(S,G,I) switched from false to true */
1305 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1307 /* AssertTrackingDesired(S,G,I) switched from true to false */
1308 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1310 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1311 assert_action_a5(ch
);
1317 * If we have a new pim interface, check to
1318 * see if any of the pre-existing channels have
1319 * their upstream out that way and turn on forwarding
1320 * for that ifchannel then.
1322 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1324 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1325 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1326 struct interface
*ifp
;
1328 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1329 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1330 struct pim_ifchannel
*ch
;
1335 if (new_pim_ifp
== loop_pim_ifp
)
1338 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1339 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1340 struct pim_upstream
*up
= ch
->upstream
;
1341 if ((!up
->channel_oil
)
1342 && (up
->rpf
.source_nexthop
1343 .interface
== new_ifp
))
1344 pim_forward_start(ch
);
1351 * Downstream per-interface (S,G,rpt) state machine
1352 * states that we need to move (S,G,rpt) items
1353 * into different states at the start of the
1354 * reception of a *,G join as well, when
1355 * we get End of Message
1357 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1360 bool send_upstream_starg
= false;
1361 struct pim_ifchannel
*child
;
1362 struct listnode
*ch_node
, *nch_node
;
1363 struct pim_instance
*pim
=
1364 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1365 struct pim_upstream
*starup
= ch
->upstream
;
1367 if (PIM_DEBUG_PIM_TRACE
)
1369 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1370 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1371 ch
->sg_str
, eom
, join
);
1375 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1376 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1379 switch (child
->ifjoin_state
) {
1380 case PIM_IFJOIN_NOINFO
:
1381 case PIM_IFJOIN_JOIN
:
1383 case PIM_IFJOIN_PRUNE
:
1385 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1387 case PIM_IFJOIN_PRUNE_PENDING
:
1389 child
->ifjoin_state
=
1390 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1392 case PIM_IFJOIN_PRUNE_TMP
:
1393 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1397 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1398 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1399 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1401 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1402 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1404 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1405 (!pim_upstream_empty_inherited_olist(
1406 child
->upstream
))) {
1407 pim_channel_add_oif(
1408 child
->upstream
->channel_oil
,
1409 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1410 pim_upstream_switch(pim
, child
->upstream
,
1411 PIM_UPSTREAM_JOINED
);
1412 pim_jp_agg_single_upstream_send(
1413 &child
->upstream
->rpf
, child
->upstream
,
1416 send_upstream_starg
= true;
1418 delete_on_noinfo(child
);
1423 if (send_upstream_starg
)
1424 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1427 unsigned int pim_ifchannel_hash_key(const void *arg
)
1429 const struct pim_ifchannel
*ch
= arg
;
1431 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);