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 (pim_upstream_evaluate_join_desired_interface(
146 ch
->upstream
, ch
, ch
->parent
))
147 pim_channel_add_oif(ch
->upstream
->channel_oil
,
148 ch
->interface
, mask
);
150 pim_channel_del_oif(ch
->upstream
->channel_oil
,
151 ch
->interface
, mask
);
153 * Do we have any S,G's that are inheriting?
154 * Nuke from on high too.
156 if (ch
->upstream
->sources
) {
157 struct pim_upstream
*child
;
158 struct listnode
*up_node
;
160 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
162 pim_channel_del_oif(child
->channel_oil
,
164 PIM_OIF_FLAG_PROTO_STAR
);
169 * When this channel is removed
170 * we need to find all our children
171 * and make sure our pointers are fixed
173 pim_ifchannel_remove_children(ch
);
176 list_delete(&ch
->sources
);
178 listnode_delete(ch
->upstream
->ifchannels
, ch
);
180 if (ch
->ifjoin_state
!= PIM_IFJOIN_NOINFO
) {
181 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
184 /* upstream is common across ifchannels, check if upstream's
185 ifchannel list is empty before deleting upstream_del
186 ref count will take care of it.
188 if (ch
->upstream
->ref_count
> 0)
189 pim_upstream_del(pim_ifp
->pim
, ch
->upstream
,
190 __PRETTY_FUNCTION__
);
193 zlog_warn("%s: Avoiding deletion of upstream with ref_count %d "
194 "from ifchannel(%s): %s", __PRETTY_FUNCTION__
,
195 ch
->upstream
->ref_count
, ch
->interface
->name
,
200 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
201 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
202 THREAD_OFF(ch
->t_ifassert_timer
);
205 listnode_delete(ch
->parent
->sources
, ch
);
209 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
211 if (PIM_DEBUG_PIM_TRACE
)
212 zlog_debug("%s: ifchannel entry %s is deleted ",
213 __PRETTY_FUNCTION__
, ch
->sg_str
);
215 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
218 void pim_ifchannel_delete_all(struct interface
*ifp
)
220 struct pim_interface
*pim_ifp
;
221 struct pim_ifchannel
*ch
;
227 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
228 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
230 pim_ifchannel_delete(ch
);
234 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
236 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
237 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
238 && ch
->t_ifjoin_expiry_timer
== NULL
)
239 pim_ifchannel_delete(ch
);
242 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
243 enum pim_ifjoin_state new_state
)
245 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
246 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
248 if (PIM_DEBUG_PIM_EVENTS
)
250 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
251 ch
->interface
->name
, ch
->sg_str
,
252 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
253 pim_ifchannel_ifjoin_name(new_state
, 0));
256 if (old_state
== new_state
) {
257 if (PIM_DEBUG_PIM_EVENTS
) {
259 "%s calledby %s: non-transition on state %d (%s)",
260 __PRETTY_FUNCTION__
, caller
, new_state
,
261 pim_ifchannel_ifjoin_name(new_state
, 0));
266 ch
->ifjoin_state
= new_state
;
268 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
269 struct pim_upstream
*up
= ch
->upstream
;
270 struct pim_upstream
*child
;
271 struct listnode
*up_node
;
274 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
275 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
277 struct channel_oil
*c_oil
=
280 if (PIM_DEBUG_PIM_TRACE
)
282 "%s %s: Prune(S,G)=%s from %s",
290 if (!pim_upstream_evaluate_join_desired(
291 pim_ifp
->pim
, child
)) {
293 c_oil
, ch
->interface
,
294 PIM_OIF_FLAG_PROTO_STAR
);
295 pim_upstream_update_join_desired(
296 pim_ifp
->pim
, child
);
300 * If the S,G has no if channel and the
302 * has output here then the *,G was
303 * supplying the implied
304 * if channel. So remove it.
305 * I think this is dead code now. is it?
307 if (c_oil
->oil
.mfcc_ttls
308 [pim_ifp
->mroute_vif_index
])
310 c_oil
, ch
->interface
,
311 PIM_OIF_FLAG_PROTO_STAR
);
314 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
315 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
317 if (PIM_DEBUG_PIM_TRACE
)
319 "%s %s: Join(S,G)=%s from %s",
325 if (pim_upstream_evaluate_join_desired(
326 pim_ifp
->pim
, child
)) {
330 PIM_OIF_FLAG_PROTO_STAR
);
331 pim_upstream_update_join_desired(
332 pim_ifp
->pim
, child
);
338 /* Transition to/from NOINFO ? */
339 if ((old_state
== PIM_IFJOIN_NOINFO
)
340 || (new_state
== PIM_IFJOIN_NOINFO
)) {
342 if (PIM_DEBUG_PIM_EVENTS
) {
343 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
344 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
346 ch
->sg_str
, ch
->interface
->name
);
350 Record uptime of state transition to/from NOINFO
352 ch
->ifjoin_creation
= pim_time_monotonic_sec();
354 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
355 pim_ifchannel_update_could_assert(ch
);
356 pim_ifchannel_update_assert_tracking_desired(ch
);
360 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
363 switch (ifjoin_state
) {
364 case PIM_IFJOIN_NOINFO
:
365 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
370 case PIM_IFJOIN_JOIN
:
373 case PIM_IFJOIN_PRUNE
:
374 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
379 case PIM_IFJOIN_PRUNE_PENDING
:
380 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
385 case PIM_IFJOIN_PRUNE_TMP
:
386 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
391 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
392 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
399 return "ifjoin_bad_state";
402 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
404 switch (ifassert_state
) {
405 case PIM_IFASSERT_NOINFO
:
407 case PIM_IFASSERT_I_AM_WINNER
:
409 case PIM_IFASSERT_I_AM_LOSER
:
413 return "ifassert_bad_state";
417 RFC 4601: 4.6.5. Assert State Macros
419 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
420 defaults to Infinity when in the NoInfo state.
422 void reset_ifassert_state(struct pim_ifchannel
*ch
)
424 struct in_addr any
= {.s_addr
= INADDR_ANY
};
426 THREAD_OFF(ch
->t_ifassert_timer
);
428 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
429 router
->infinite_assert_metric
);
432 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
433 struct prefix_sg
*sg
)
435 struct pim_interface
*pim_ifp
;
436 struct pim_ifchannel
*ch
;
437 struct pim_ifchannel lookup
;
442 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
443 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
448 lookup
.interface
= ifp
;
449 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
454 static void ifmembership_set(struct pim_ifchannel
*ch
,
455 enum pim_ifmembership membership
)
457 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
459 if (ch
->local_ifmembership
== membership
)
462 if (PIM_DEBUG_PIM_EVENTS
) {
463 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
464 __PRETTY_FUNCTION__
, ch
->sg_str
,
465 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
467 ch
->interface
->name
);
470 ch
->local_ifmembership
= membership
;
472 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
473 pim_ifchannel_update_could_assert(ch
);
474 pim_ifchannel_update_assert_tracking_desired(ch
);
478 void pim_ifchannel_membership_clear(struct interface
*ifp
)
480 struct pim_interface
*pim_ifp
;
481 struct pim_ifchannel
*ch
;
486 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
487 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
490 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
492 struct pim_interface
*pim_ifp
;
493 struct pim_ifchannel
*ch
, *ch_tmp
;
498 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
499 delete_on_noinfo(ch
);
503 * For a given Interface, if we are given a S,G
504 * Find the *,G (If we have it).
505 * If we are passed a *,G, find the *,* ifchannel
508 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
510 struct prefix_sg parent_sg
= ch
->sg
;
511 struct pim_ifchannel
*parent
= NULL
;
514 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
515 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
516 parent_sg
.src
.s_addr
= INADDR_ANY
;
517 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
520 listnode_add(parent
->sources
, ch
);
527 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
528 struct prefix_sg
*sg
,
529 uint8_t source_flags
, int up_flags
)
531 struct pim_interface
*pim_ifp
;
532 struct pim_ifchannel
*ch
;
533 struct pim_upstream
*up
;
535 ch
= pim_ifchannel_find(ifp
, sg
);
541 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
544 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
545 && !(source_flags
& PIM_ENCODE_WC_BIT
))
546 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
550 pim_str_sg_set(sg
, ch
->sg_str
);
551 ch
->parent
= pim_ifchannel_find_parent(ch
);
552 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
553 ch
->sources
= list_new();
555 (int (*)(void *, void *))pim_ifchannel_compare
;
559 pim_ifchannel_find_new_children(ch
);
560 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
562 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
563 ch
->t_ifjoin_expiry_timer
= NULL
;
564 ch
->t_ifjoin_prune_pending_timer
= NULL
;
565 ch
->ifjoin_creation
= 0;
567 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
569 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
,
570 __PRETTY_FUNCTION__
, ch
);
574 listnode_add_sort(up
->ifchannels
, ch
);
576 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
577 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
579 ch
->ifassert_winner
.s_addr
= 0;
582 ch
->t_ifassert_timer
= NULL
;
583 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
584 reset_ifassert_state(ch
);
585 if (pim_macro_ch_could_assert_eval(ch
))
586 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
588 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
590 if (pim_macro_assert_tracking_desired_eval(ch
))
591 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
593 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
595 if (PIM_DEBUG_PIM_TRACE
)
596 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
602 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
604 pim_forward_stop(ch
, !ch_del
);
605 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
607 delete_on_noinfo(ch
);
610 static int on_ifjoin_expiry_timer(struct thread
*t
)
612 struct pim_ifchannel
*ch
;
617 zlog_debug("%s: ifchannel %s expiry timer", __PRETTY_FUNCTION__
,
620 ifjoin_to_noinfo(ch
, true);
621 /* ch may have been deleted */
626 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
628 struct pim_ifchannel
*ch
;
629 int send_prune_echo
; /* boolean */
630 struct interface
*ifp
;
631 struct pim_interface
*pim_ifp
;
637 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
638 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
639 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
641 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
644 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
645 /* Send PruneEcho(S,G) ? */
647 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
649 if (send_prune_echo
) {
652 rpf
.source_nexthop
.interface
= ifp
;
653 rpf
.rpf_addr
.u
.prefix4
=
654 pim_ifp
->primary_address
;
655 pim_jp_agg_single_upstream_send(
656 &rpf
, ch
->upstream
, 0);
659 ifjoin_to_noinfo(ch
, true);
661 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
662 * message on RP path upon prune timer expiry.
664 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
666 struct pim_upstream
*parent
=
667 ch
->upstream
->parent
;
669 pim_upstream_update_join_desired(pim_ifp
->pim
,
672 pim_jp_agg_single_upstream_send(&parent
->rpf
,
676 /* from here ch may have been deleted */
682 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
683 struct in_addr upstream
, struct prefix_sg
*sg
,
684 uint8_t source_flags
, int holdtime
)
686 struct pim_upstream
*up
;
687 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
689 /* Upstream (S,G) in Joined state ? */
690 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
693 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
696 /* Upstream (S,G) in Joined state */
698 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
699 /* RPF'(S,G) not found */
700 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
701 __PRETTY_FUNCTION__
, up
->sg_str
);
705 /* upstream directed to RPF'(S,G) ? */
706 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
707 char up_str
[INET_ADDRSTRLEN
];
708 char rpf_str
[PREFIX_STRLEN
];
709 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
710 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
713 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
714 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
715 rpf_str
, recv_ifp
->name
);
718 /* upstream directed to RPF'(S,G) */
721 /* Join(S,G) to RPF'(S,G) */
722 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
727 /* Prune to RPF'(S,G) */
729 if (source_flags
& PIM_RPT_BIT_MASK
) {
730 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
731 /* Prune(*,G) to RPF'(S,G) */
732 pim_upstream_join_timer_decrease_to_t_override(
737 /* Prune(S,G,rpt) to RPF'(S,G) */
738 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
743 /* Prune(S,G) to RPF'(S,G) */
744 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
747 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
748 struct in_addr upstream
, struct prefix_sg
*sg
,
749 uint8_t source_flags
, uint16_t holdtime
)
751 struct pim_interface
*recv_pim_ifp
;
752 int is_local
; /* boolean */
754 recv_pim_ifp
= recv_ifp
->info
;
755 zassert(recv_pim_ifp
);
757 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
762 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
763 char up_str
[INET_ADDRSTRLEN
];
764 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
765 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
766 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
767 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
771 * Since recv upstream addr was not directed to our primary
772 * address, check if we should react to it in any way.
774 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
777 return 1; /* non-local */
780 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
781 struct in_addr upstream
, struct prefix_sg
*sg
,
782 uint8_t source_flags
, uint16_t holdtime
)
784 struct pim_interface
*pim_ifp
;
785 struct pim_ifchannel
*ch
;
787 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
792 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
793 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
798 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
800 Transitions from "I am Assert Loser" State
802 Receive Join(S,G) on Interface I
804 We receive a Join(S,G) that has the Upstream Neighbor Address
805 field set to my primary IP address on interface I. The action is
806 to transition to NoInfo state, delete this (S,G) assert state
807 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
810 Notice: The nonlocal_upstream() test above ensures the upstream
811 address of the join message is our primary address.
813 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
814 char neigh_str
[INET_ADDRSTRLEN
];
815 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
817 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
818 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
821 assert_action_a5(ch
);
827 switch (ch
->ifjoin_state
) {
828 case PIM_IFJOIN_NOINFO
:
829 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
831 if (pim_macro_chisin_oiflist(ch
)) {
832 pim_upstream_inherited_olist(pim_ifp
->pim
,
834 pim_forward_start(ch
);
837 * If we are going to be a LHR, we need to note it
839 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
840 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
841 && !(ch
->upstream
->flags
842 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
843 pim_upstream_ref(ch
->upstream
,
844 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
845 __PRETTY_FUNCTION__
);
846 pim_upstream_keep_alive_timer_start(
847 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
850 case PIM_IFJOIN_JOIN
:
851 zassert(!ch
->t_ifjoin_prune_pending_timer
);
854 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
856 previously received join message with holdtime=0xFFFF.
858 if (ch
->t_ifjoin_expiry_timer
) {
859 unsigned long remain
= thread_timer_remain_second(
860 ch
->t_ifjoin_expiry_timer
);
861 if (remain
> holdtime
) {
863 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
866 Transitions from Join State
868 The (S,G) downstream state machine on
869 interface I remains in
870 Join state, and the Expiry Timer (ET) is
872 maximum of its current value and the HoldTime
874 triggering Join/Prune message.
876 Conclusion: Do not change the ET if the
878 higher than the received join holdtime.
883 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
885 case PIM_IFJOIN_PRUNE
:
886 if (source_flags
& PIM_ENCODE_RPT_BIT
)
887 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
891 * We have received a S,G join and we are in
892 * S,G RPT Prune state. Which means we need
893 * to transition to Join state and setup
894 * state as appropriate.
896 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
898 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
899 if (pim_upstream_evaluate_join_desired(pim_ifp
->pim
,
901 pim_channel_add_oif(ch
->upstream
->channel_oil
,
903 PIM_OIF_FLAG_PROTO_PIM
);
904 pim_upstream_update_join_desired(pim_ifp
->pim
,
909 case PIM_IFJOIN_PRUNE_PENDING
:
910 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
911 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
912 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
913 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
916 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
919 case PIM_IFJOIN_PRUNE_TMP
:
921 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
925 if (holdtime
!= 0xFFFF) {
926 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
927 holdtime
, &ch
->t_ifjoin_expiry_timer
);
931 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
932 struct prefix_sg
*sg
, uint8_t source_flags
,
935 struct pim_ifchannel
*ch
;
936 struct pim_interface
*pim_ifp
;
937 int jp_override_interval_msec
;
939 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
944 ch
= pim_ifchannel_find(ifp
, sg
);
945 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
948 "%s: Received prune with no relevant ifchannel %s%s state: %d",
949 __PRETTY_FUNCTION__
, ifp
->name
,
950 pim_str_sg_dump(sg
), source_flags
);
954 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
955 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 if (PIM_DEBUG_EVENTS
)
1087 zlog_debug("%s:%s Unable to add ifchannel",
1088 __PRETTY_FUNCTION__
,
1089 pim_str_sg_dump(sg
));
1093 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1095 if (sg
->src
.s_addr
== INADDR_ANY
) {
1096 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1097 struct pim_upstream
*child
;
1098 struct listnode
*up_node
;
1102 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1103 if (PIM_DEBUG_EVENTS
)
1104 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1105 __FILE__
, __PRETTY_FUNCTION__
,
1106 child
->sg_str
, ifp
->name
,
1109 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1110 if (pim_upstream_evaluate_join_desired_interface(
1111 child
, ch
, starch
)) {
1112 pim_channel_add_oif(child
->channel_oil
, ifp
,
1113 PIM_OIF_FLAG_PROTO_STAR
);
1114 pim_upstream_switch(pim
, child
,
1115 PIM_UPSTREAM_JOINED
);
1119 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1120 if (pim
->spt
.plist
) {
1121 struct prefix_list
*plist
= prefix_list_lookup(
1122 AFI_IP
, pim
->spt
.plist
);
1125 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1126 g
.u
.prefix4
= up
->sg
.grp
;
1128 if (prefix_list_apply(plist
, &g
)
1130 pim_channel_add_oif(
1131 up
->channel_oil
, pim
->regiface
,
1132 PIM_OIF_FLAG_PROTO_IGMP
);
1136 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1137 PIM_OIF_FLAG_PROTO_IGMP
);
1143 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1144 struct prefix_sg
*sg
)
1146 struct pim_ifchannel
*starch
, *ch
, *orig
;
1147 struct pim_interface
*pim_ifp
;
1149 /* PIM enabled on interface? */
1150 pim_ifp
= ifp
->info
;
1153 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1156 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1159 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1161 if (sg
->src
.s_addr
== INADDR_ANY
) {
1162 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1163 struct pim_upstream
*child
;
1164 struct listnode
*up_node
, *up_nnode
;
1168 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1169 struct channel_oil
*c_oil
= child
->channel_oil
;
1170 struct pim_ifchannel
*chchannel
=
1171 pim_ifchannel_find(ifp
, &child
->sg
);
1173 pim_ifp
= ifp
->info
;
1175 if (PIM_DEBUG_EVENTS
)
1176 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1177 __FILE__
, __PRETTY_FUNCTION__
,
1178 up
->sg_str
, ifp
->name
,
1181 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1183 && !pim_upstream_evaluate_join_desired_interface(
1185 pim_channel_del_oif(c_oil
, ifp
,
1186 PIM_OIF_FLAG_PROTO_STAR
);
1189 * If the S,G has no if channel and the c_oil still
1190 * has output here then the *,G was supplying the
1192 * if channel. So remove it.
1194 if (!chchannel
&& c_oil
1195 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1196 pim_channel_del_oif(c_oil
, ifp
,
1197 PIM_OIF_FLAG_PROTO_STAR
);
1199 /* Child node removal/ref count-- will happen as part of
1200 * parent' delete_no_info */
1203 delete_on_noinfo(orig
);
1206 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1208 int old_couldassert
=
1209 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1210 int new_couldassert
=
1211 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1213 if (new_couldassert
== old_couldassert
)
1216 if (PIM_DEBUG_PIM_EVENTS
) {
1217 char src_str
[INET_ADDRSTRLEN
];
1218 char grp_str
[INET_ADDRSTRLEN
];
1219 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1220 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1221 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1222 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1223 ch
->interface
->name
, old_couldassert
,
1227 if (new_couldassert
) {
1228 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1229 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1231 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1232 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1234 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1235 assert_action_a4(ch
);
1239 pim_ifchannel_update_my_assert_metric(ch
);
1243 my_assert_metric may be affected by:
1246 pim_ifp->primary_address
1247 rpf->source_nexthop.mrib_metric_preference;
1248 rpf->source_nexthop.mrib_route_metric;
1250 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1252 struct pim_assert_metric my_metric_new
=
1253 pim_macro_ch_my_assert_metric_eval(ch
);
1255 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1258 if (PIM_DEBUG_PIM_EVENTS
) {
1259 char src_str
[INET_ADDRSTRLEN
];
1260 char grp_str
[INET_ADDRSTRLEN
];
1261 char old_addr_str
[INET_ADDRSTRLEN
];
1262 char new_addr_str
[INET_ADDRSTRLEN
];
1263 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1264 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1265 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1266 old_addr_str
, sizeof(old_addr_str
));
1267 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1268 new_addr_str
, sizeof(new_addr_str
));
1270 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1271 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1272 ch
->interface
->name
,
1273 ch
->ifassert_my_metric
.rpt_bit_flag
,
1274 ch
->ifassert_my_metric
.metric_preference
,
1275 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1276 my_metric_new
.rpt_bit_flag
,
1277 my_metric_new
.metric_preference
,
1278 my_metric_new
.route_metric
, new_addr_str
);
1281 ch
->ifassert_my_metric
= my_metric_new
;
1283 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1284 &ch
->ifassert_winner_metric
)) {
1285 assert_action_a5(ch
);
1289 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1291 int old_atd
= PIM_FORCE_BOOLEAN(
1292 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1294 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1296 if (new_atd
== old_atd
)
1299 if (PIM_DEBUG_PIM_EVENTS
) {
1300 char src_str
[INET_ADDRSTRLEN
];
1301 char grp_str
[INET_ADDRSTRLEN
];
1302 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1303 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1305 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1306 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1307 ch
->interface
->name
, old_atd
, new_atd
);
1311 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1312 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1314 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1315 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1317 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1318 assert_action_a5(ch
);
1324 * If we have a new pim interface, check to
1325 * see if any of the pre-existing channels have
1326 * their upstream out that way and turn on forwarding
1327 * for that ifchannel then.
1329 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1331 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1332 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1333 struct interface
*ifp
;
1335 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1336 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1337 struct pim_ifchannel
*ch
;
1342 if (new_pim_ifp
== loop_pim_ifp
)
1345 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1346 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1347 struct pim_upstream
*up
= ch
->upstream
;
1348 if ((!up
->channel_oil
)
1349 && (up
->rpf
.source_nexthop
1350 .interface
== new_ifp
))
1351 pim_forward_start(ch
);
1358 * Downstream per-interface (S,G,rpt) state machine
1359 * states that we need to move (S,G,rpt) items
1360 * into different states at the start of the
1361 * reception of a *,G join as well, when
1362 * we get End of Message
1364 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1367 bool send_upstream_starg
= false;
1368 struct pim_ifchannel
*child
;
1369 struct listnode
*ch_node
, *nch_node
;
1370 struct pim_instance
*pim
=
1371 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1372 struct pim_upstream
*starup
= ch
->upstream
;
1374 if (PIM_DEBUG_PIM_TRACE
)
1376 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1377 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1378 ch
->sg_str
, eom
, join
);
1382 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1383 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1386 switch (child
->ifjoin_state
) {
1387 case PIM_IFJOIN_NOINFO
:
1388 case PIM_IFJOIN_JOIN
:
1390 case PIM_IFJOIN_PRUNE
:
1392 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1394 case PIM_IFJOIN_PRUNE_PENDING
:
1396 child
->ifjoin_state
=
1397 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1399 case PIM_IFJOIN_PRUNE_TMP
:
1400 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1404 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1405 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1406 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1408 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1409 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1411 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1412 (!pim_upstream_empty_inherited_olist(
1413 child
->upstream
))) {
1414 pim_channel_add_oif(
1415 child
->upstream
->channel_oil
,
1416 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1417 pim_upstream_switch(pim
, child
->upstream
,
1418 PIM_UPSTREAM_JOINED
);
1419 pim_jp_agg_single_upstream_send(
1420 &child
->upstream
->rpf
, child
->upstream
,
1423 send_upstream_starg
= true;
1425 delete_on_noinfo(child
);
1430 if (send_upstream_starg
)
1431 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1434 unsigned int pim_ifchannel_hash_key(const void *arg
)
1436 const struct pim_ifchannel
*ch
= arg
;
1438 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);