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 if (PIM_DEBUG_PIM_TRACE
)
196 zlog_debug("%s: Avoiding deletion of upstream with ref_count %d "
197 "from ifchannel(%s): %s", __PRETTY_FUNCTION__
,
198 ch
->upstream
->ref_count
, ch
->interface
->name
,
204 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
205 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
206 THREAD_OFF(ch
->t_ifassert_timer
);
209 listnode_delete(ch
->parent
->sources
, ch
);
213 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
215 if (PIM_DEBUG_PIM_TRACE
)
216 zlog_debug("%s: ifchannel entry %s is deleted ",
217 __PRETTY_FUNCTION__
, ch
->sg_str
);
219 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
222 void pim_ifchannel_delete_all(struct interface
*ifp
)
224 struct pim_interface
*pim_ifp
;
225 struct pim_ifchannel
*ch
;
231 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
232 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
234 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
,
235 ch
, PIM_IFJOIN_NOINFO
);
236 pim_ifchannel_delete(ch
);
240 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
242 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
243 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
244 && ch
->t_ifjoin_expiry_timer
== NULL
)
245 pim_ifchannel_delete(ch
);
248 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
249 enum pim_ifjoin_state new_state
)
251 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
252 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
254 if (PIM_DEBUG_PIM_EVENTS
)
256 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
257 ch
->interface
->name
, ch
->sg_str
,
258 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
259 pim_ifchannel_ifjoin_name(new_state
, 0));
262 if (old_state
== new_state
) {
263 if (PIM_DEBUG_PIM_EVENTS
) {
265 "%s calledby %s: non-transition on state %d (%s)",
266 __PRETTY_FUNCTION__
, caller
, new_state
,
267 pim_ifchannel_ifjoin_name(new_state
, 0));
272 ch
->ifjoin_state
= new_state
;
274 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
275 struct pim_upstream
*up
= ch
->upstream
;
276 struct pim_upstream
*child
;
277 struct listnode
*up_node
;
280 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
281 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
283 struct channel_oil
*c_oil
=
286 if (PIM_DEBUG_PIM_TRACE
)
288 "%s %s: Prune(S,G)=%s from %s",
296 if (!pim_upstream_evaluate_join_desired(
297 pim_ifp
->pim
, child
)) {
299 c_oil
, ch
->interface
,
300 PIM_OIF_FLAG_PROTO_STAR
);
301 pim_upstream_update_join_desired(
302 pim_ifp
->pim
, child
);
306 * If the S,G has no if channel and the
308 * has output here then the *,G was
309 * supplying the implied
310 * if channel. So remove it.
311 * I think this is dead code now. is it?
313 if (c_oil
->oil
.mfcc_ttls
314 [pim_ifp
->mroute_vif_index
])
316 c_oil
, ch
->interface
,
317 PIM_OIF_FLAG_PROTO_STAR
);
320 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
321 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
323 if (PIM_DEBUG_PIM_TRACE
)
325 "%s %s: Join(S,G)=%s from %s",
331 if (pim_upstream_evaluate_join_desired(
332 pim_ifp
->pim
, child
)) {
336 PIM_OIF_FLAG_PROTO_STAR
);
337 pim_upstream_update_join_desired(
338 pim_ifp
->pim
, child
);
344 /* Transition to/from NOINFO ? */
345 if ((old_state
== PIM_IFJOIN_NOINFO
)
346 || (new_state
== PIM_IFJOIN_NOINFO
)) {
348 if (PIM_DEBUG_PIM_EVENTS
) {
349 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
350 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
352 ch
->sg_str
, ch
->interface
->name
);
356 Record uptime of state transition to/from NOINFO
358 ch
->ifjoin_creation
= pim_time_monotonic_sec();
360 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
361 pim_ifchannel_update_could_assert(ch
);
362 pim_ifchannel_update_assert_tracking_desired(ch
);
366 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
369 switch (ifjoin_state
) {
370 case PIM_IFJOIN_NOINFO
:
371 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
376 case PIM_IFJOIN_JOIN
:
379 case PIM_IFJOIN_PRUNE
:
380 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
385 case PIM_IFJOIN_PRUNE_PENDING
:
386 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
391 case PIM_IFJOIN_PRUNE_TMP
:
392 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
397 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
398 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
405 return "ifjoin_bad_state";
408 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
410 switch (ifassert_state
) {
411 case PIM_IFASSERT_NOINFO
:
413 case PIM_IFASSERT_I_AM_WINNER
:
415 case PIM_IFASSERT_I_AM_LOSER
:
419 return "ifassert_bad_state";
423 RFC 4601: 4.6.5. Assert State Macros
425 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
426 defaults to Infinity when in the NoInfo state.
428 void reset_ifassert_state(struct pim_ifchannel
*ch
)
430 struct in_addr any
= {.s_addr
= INADDR_ANY
};
432 THREAD_OFF(ch
->t_ifassert_timer
);
434 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
435 router
->infinite_assert_metric
);
438 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
439 struct prefix_sg
*sg
)
441 struct pim_interface
*pim_ifp
;
442 struct pim_ifchannel
*ch
;
443 struct pim_ifchannel lookup
;
448 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
449 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
), ifp
->name
);
454 lookup
.interface
= ifp
;
455 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
460 static void ifmembership_set(struct pim_ifchannel
*ch
,
461 enum pim_ifmembership membership
)
463 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
465 if (ch
->local_ifmembership
== membership
)
468 if (PIM_DEBUG_PIM_EVENTS
) {
469 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
470 __PRETTY_FUNCTION__
, ch
->sg_str
,
471 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
473 ch
->interface
->name
);
476 ch
->local_ifmembership
= membership
;
478 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
479 pim_ifchannel_update_could_assert(ch
);
480 pim_ifchannel_update_assert_tracking_desired(ch
);
484 void pim_ifchannel_membership_clear(struct interface
*ifp
)
486 struct pim_interface
*pim_ifp
;
487 struct pim_ifchannel
*ch
;
492 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
493 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
496 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
498 struct pim_interface
*pim_ifp
;
499 struct pim_ifchannel
*ch
, *ch_tmp
;
504 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
505 delete_on_noinfo(ch
);
509 * For a given Interface, if we are given a S,G
510 * Find the *,G (If we have it).
511 * If we are passed a *,G, find the *,* ifchannel
514 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
516 struct prefix_sg parent_sg
= ch
->sg
;
517 struct pim_ifchannel
*parent
= NULL
;
520 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
521 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
522 parent_sg
.src
.s_addr
= INADDR_ANY
;
523 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
526 listnode_add(parent
->sources
, ch
);
533 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
534 struct prefix_sg
*sg
,
535 uint8_t source_flags
, int up_flags
)
537 struct pim_interface
*pim_ifp
;
538 struct pim_ifchannel
*ch
;
539 struct pim_upstream
*up
;
541 ch
= pim_ifchannel_find(ifp
, sg
);
547 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
550 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
551 && !(source_flags
& PIM_ENCODE_WC_BIT
))
552 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
556 pim_str_sg_set(sg
, ch
->sg_str
);
557 ch
->parent
= pim_ifchannel_find_parent(ch
);
558 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
559 ch
->sources
= list_new();
561 (int (*)(void *, void *))pim_ifchannel_compare
;
565 pim_ifchannel_find_new_children(ch
);
566 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
568 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
569 ch
->t_ifjoin_expiry_timer
= NULL
;
570 ch
->t_ifjoin_prune_pending_timer
= NULL
;
571 ch
->ifjoin_creation
= 0;
573 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
575 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
,
576 __PRETTY_FUNCTION__
, ch
);
580 listnode_add_sort(up
->ifchannels
, ch
);
582 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
583 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
585 ch
->ifassert_winner
.s_addr
= 0;
588 ch
->t_ifassert_timer
= NULL
;
589 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
590 reset_ifassert_state(ch
);
591 if (pim_macro_ch_could_assert_eval(ch
))
592 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
594 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
596 if (pim_macro_assert_tracking_desired_eval(ch
))
597 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
599 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
601 if (PIM_DEBUG_PIM_TRACE
)
602 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__
,
608 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
610 pim_forward_stop(ch
, !ch_del
);
611 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
, PIM_IFJOIN_NOINFO
);
613 delete_on_noinfo(ch
);
616 static int on_ifjoin_expiry_timer(struct thread
*t
)
618 struct pim_ifchannel
*ch
;
622 if (PIM_DEBUG_PIM_TRACE
)
623 zlog_debug("%s: ifchannel %s expiry timer", __PRETTY_FUNCTION__
,
626 ifjoin_to_noinfo(ch
, true);
627 /* ch may have been deleted */
632 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
634 struct pim_ifchannel
*ch
;
635 int send_prune_echo
; /* boolean */
636 struct interface
*ifp
;
637 struct pim_interface
*pim_ifp
;
641 if (PIM_DEBUG_PIM_TRACE
)
643 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
644 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
645 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
647 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
650 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
651 /* Send PruneEcho(S,G) ? */
653 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
655 if (send_prune_echo
) {
658 rpf
.source_nexthop
.interface
= ifp
;
659 rpf
.rpf_addr
.u
.prefix4
=
660 pim_ifp
->primary_address
;
661 pim_jp_agg_single_upstream_send(
662 &rpf
, ch
->upstream
, 0);
665 ifjoin_to_noinfo(ch
, true);
667 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
668 * message on RP path upon prune timer expiry.
670 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
672 struct pim_upstream
*parent
=
673 ch
->upstream
->parent
;
675 pim_upstream_update_join_desired(pim_ifp
->pim
,
678 pim_jp_agg_single_upstream_send(&parent
->rpf
,
682 /* from here ch may have been deleted */
688 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
689 struct in_addr upstream
, struct prefix_sg
*sg
,
690 uint8_t source_flags
, int holdtime
)
692 struct pim_upstream
*up
;
693 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
695 /* Upstream (S,G) in Joined state ? */
696 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
699 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
702 /* Upstream (S,G) in Joined state */
704 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
705 /* RPF'(S,G) not found */
706 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
707 __PRETTY_FUNCTION__
, up
->sg_str
);
711 /* upstream directed to RPF'(S,G) ? */
712 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
713 char up_str
[INET_ADDRSTRLEN
];
714 char rpf_str
[PREFIX_STRLEN
];
715 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
716 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
719 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
720 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
721 rpf_str
, recv_ifp
->name
);
724 /* upstream directed to RPF'(S,G) */
727 /* Join(S,G) to RPF'(S,G) */
728 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
733 /* Prune to RPF'(S,G) */
735 if (source_flags
& PIM_RPT_BIT_MASK
) {
736 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
737 /* Prune(*,G) to RPF'(S,G) */
738 pim_upstream_join_timer_decrease_to_t_override(
743 /* Prune(S,G,rpt) to RPF'(S,G) */
744 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
749 /* Prune(S,G) to RPF'(S,G) */
750 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
753 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
754 struct in_addr upstream
, struct prefix_sg
*sg
,
755 uint8_t source_flags
, uint16_t holdtime
)
757 struct pim_interface
*recv_pim_ifp
;
758 int is_local
; /* boolean */
760 recv_pim_ifp
= recv_ifp
->info
;
761 zassert(recv_pim_ifp
);
763 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
768 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
769 char up_str
[INET_ADDRSTRLEN
];
770 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
771 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
772 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
773 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
777 * Since recv upstream addr was not directed to our primary
778 * address, check if we should react to it in any way.
780 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
783 return 1; /* non-local */
786 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
787 struct in_addr upstream
, struct prefix_sg
*sg
,
788 uint8_t source_flags
, uint16_t holdtime
)
790 struct pim_interface
*pim_ifp
;
791 struct pim_ifchannel
*ch
;
793 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
798 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
799 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
802 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
804 Transitions from "I am Assert Loser" State
806 Receive Join(S,G) on Interface I
808 We receive a Join(S,G) that has the Upstream Neighbor Address
809 field set to my primary IP address on interface I. The action is
810 to transition to NoInfo state, delete this (S,G) assert state
811 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
814 Notice: The nonlocal_upstream() test above ensures the upstream
815 address of the join message is our primary address.
817 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
818 char neigh_str
[INET_ADDRSTRLEN
];
819 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
821 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
822 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
825 assert_action_a5(ch
);
831 switch (ch
->ifjoin_state
) {
832 case PIM_IFJOIN_NOINFO
:
833 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
835 if (pim_macro_chisin_oiflist(ch
)) {
836 pim_upstream_inherited_olist(pim_ifp
->pim
,
838 pim_forward_start(ch
);
841 * If we are going to be a LHR, we need to note it
843 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
844 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
845 && !(ch
->upstream
->flags
846 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
847 pim_upstream_ref(ch
->upstream
,
848 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
849 __PRETTY_FUNCTION__
);
850 pim_upstream_keep_alive_timer_start(
851 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
854 case PIM_IFJOIN_JOIN
:
855 zassert(!ch
->t_ifjoin_prune_pending_timer
);
858 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
860 previously received join message with holdtime=0xFFFF.
862 if (ch
->t_ifjoin_expiry_timer
) {
863 unsigned long remain
= thread_timer_remain_second(
864 ch
->t_ifjoin_expiry_timer
);
865 if (remain
> holdtime
) {
867 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
870 Transitions from Join State
872 The (S,G) downstream state machine on
873 interface I remains in
874 Join state, and the Expiry Timer (ET) is
876 maximum of its current value and the HoldTime
878 triggering Join/Prune message.
880 Conclusion: Do not change the ET if the
882 higher than the received join holdtime.
887 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
889 case PIM_IFJOIN_PRUNE
:
890 if (source_flags
& PIM_ENCODE_RPT_BIT
)
891 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
895 * We have received a S,G join and we are in
896 * S,G RPT Prune state. Which means we need
897 * to transition to Join state and setup
898 * state as appropriate.
900 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
902 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
903 if (pim_upstream_evaluate_join_desired(pim_ifp
->pim
,
905 pim_channel_add_oif(ch
->upstream
->channel_oil
,
907 PIM_OIF_FLAG_PROTO_PIM
);
908 pim_upstream_update_join_desired(pim_ifp
->pim
,
913 case PIM_IFJOIN_PRUNE_PENDING
:
914 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
915 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
916 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
917 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
920 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
923 case PIM_IFJOIN_PRUNE_TMP
:
925 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
929 if (holdtime
!= 0xFFFF) {
930 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
931 holdtime
, &ch
->t_ifjoin_expiry_timer
);
935 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
936 struct prefix_sg
*sg
, uint8_t source_flags
,
939 struct pim_ifchannel
*ch
;
940 struct pim_interface
*pim_ifp
;
941 int jp_override_interval_msec
;
943 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
948 ch
= pim_ifchannel_find(ifp
, sg
);
949 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
950 if (PIM_DEBUG_PIM_TRACE
)
952 "%s: Received prune with no relevant ifchannel %s%s state: %d",
953 __PRETTY_FUNCTION__
, ifp
->name
,
954 pim_str_sg_dump(sg
), source_flags
);
958 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
959 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
963 switch (ch
->ifjoin_state
) {
964 case PIM_IFJOIN_NOINFO
:
965 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
966 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
967 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
969 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
970 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
971 jp_override_interval_msec
=
972 pim_if_jp_override_interval_msec(ifp
);
974 jp_override_interval_msec
=
975 0; /* schedule to expire immediately */
976 /* If we called ifjoin_prune() directly instead, care
978 be taken not to use "ch" afterwards since it would be
981 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
982 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
983 thread_add_timer_msec(
984 router
->master
, on_ifjoin_prune_pending_timer
,
985 ch
, jp_override_interval_msec
,
986 &ch
->t_ifjoin_prune_pending_timer
);
987 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
989 &ch
->t_ifjoin_expiry_timer
);
990 pim_upstream_update_join_desired(pim_ifp
->pim
,
994 case PIM_IFJOIN_PRUNE_PENDING
:
997 case PIM_IFJOIN_JOIN
:
998 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1000 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
1001 PIM_IFJOIN_PRUNE_PENDING
);
1003 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1004 jp_override_interval_msec
=
1005 pim_if_jp_override_interval_msec(ifp
);
1007 jp_override_interval_msec
=
1008 0; /* schedule to expire immediately */
1009 /* If we called ifjoin_prune() directly instead, care should
1010 be taken not to use "ch" afterwards since it would be
1012 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1013 thread_add_timer_msec(router
->master
,
1014 on_ifjoin_prune_pending_timer
, ch
,
1015 jp_override_interval_msec
,
1016 &ch
->t_ifjoin_prune_pending_timer
);
1018 case PIM_IFJOIN_PRUNE
:
1019 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1020 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1021 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1023 &ch
->t_ifjoin_expiry_timer
);
1026 case PIM_IFJOIN_PRUNE_TMP
:
1027 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1028 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1029 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1030 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1032 &ch
->t_ifjoin_expiry_timer
);
1035 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1036 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1037 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1038 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1039 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1041 &ch
->t_ifjoin_expiry_timer
);
1047 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1048 struct prefix_sg
*sg
)
1050 struct pim_ifchannel
*ch
, *starch
;
1051 struct pim_interface
*pim_ifp
;
1052 struct pim_instance
*pim
;
1054 /* PIM enabled on interface? */
1055 pim_ifp
= ifp
->info
;
1057 if (PIM_DEBUG_EVENTS
)
1058 zlog_debug("%s:%s Expected pim interface setup for %s",
1059 __PRETTY_FUNCTION__
,
1060 pim_str_sg_dump(sg
), ifp
->name
);
1064 if (!PIM_IF_TEST_PIM(pim_ifp
->options
)) {
1065 if (PIM_DEBUG_EVENTS
)
1066 zlog_debug("%s:%s PIM is not configured on this interface %s",
1067 __PRETTY_FUNCTION__
,
1068 pim_str_sg_dump(sg
), ifp
->name
);
1074 /* skip (*,G) ch creation if G is of type SSM */
1075 if (sg
->src
.s_addr
== INADDR_ANY
) {
1076 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1077 if (PIM_DEBUG_PIM_EVENTS
)
1079 "%s: local membership (S,G)=%s ignored as group is SSM",
1080 __PRETTY_FUNCTION__
,
1081 pim_str_sg_dump(sg
));
1086 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1088 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1090 if (sg
->src
.s_addr
== INADDR_ANY
) {
1091 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1092 struct pim_upstream
*child
;
1093 struct listnode
*up_node
;
1097 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1098 if (PIM_DEBUG_EVENTS
)
1099 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1100 __FILE__
, __PRETTY_FUNCTION__
,
1101 child
->sg_str
, ifp
->name
,
1104 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1105 if (pim_upstream_evaluate_join_desired_interface(
1106 child
, ch
, starch
)) {
1107 pim_channel_add_oif(child
->channel_oil
, ifp
,
1108 PIM_OIF_FLAG_PROTO_STAR
);
1109 pim_upstream_switch(pim
, child
,
1110 PIM_UPSTREAM_JOINED
);
1114 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1115 if (pim
->spt
.plist
) {
1116 struct prefix_list
*plist
= prefix_list_lookup(
1117 AFI_IP
, pim
->spt
.plist
);
1120 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1121 g
.u
.prefix4
= up
->sg
.grp
;
1123 if (prefix_list_apply(plist
, &g
)
1125 pim_channel_add_oif(
1126 up
->channel_oil
, pim
->regiface
,
1127 PIM_OIF_FLAG_PROTO_IGMP
);
1131 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1132 PIM_OIF_FLAG_PROTO_IGMP
);
1138 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1139 struct prefix_sg
*sg
)
1141 struct pim_ifchannel
*starch
, *ch
, *orig
;
1142 struct pim_interface
*pim_ifp
;
1144 /* PIM enabled on interface? */
1145 pim_ifp
= ifp
->info
;
1148 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1151 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1154 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1156 if (sg
->src
.s_addr
== INADDR_ANY
) {
1157 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1158 struct pim_upstream
*child
;
1159 struct listnode
*up_node
, *up_nnode
;
1163 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1164 struct channel_oil
*c_oil
= child
->channel_oil
;
1165 struct pim_ifchannel
*chchannel
=
1166 pim_ifchannel_find(ifp
, &child
->sg
);
1168 pim_ifp
= ifp
->info
;
1170 if (PIM_DEBUG_EVENTS
)
1171 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1172 __FILE__
, __PRETTY_FUNCTION__
,
1173 up
->sg_str
, ifp
->name
,
1176 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1178 && !pim_upstream_evaluate_join_desired_interface(
1180 pim_channel_del_oif(c_oil
, ifp
,
1181 PIM_OIF_FLAG_PROTO_STAR
);
1184 * If the S,G has no if channel and the c_oil still
1185 * has output here then the *,G was supplying the
1187 * if channel. So remove it.
1189 if (!chchannel
&& c_oil
1190 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1191 pim_channel_del_oif(c_oil
, ifp
,
1192 PIM_OIF_FLAG_PROTO_STAR
);
1194 /* Child node removal/ref count-- will happen as part of
1195 * parent' delete_no_info */
1198 delete_on_noinfo(orig
);
1201 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1203 int old_couldassert
=
1204 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1205 int new_couldassert
=
1206 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1208 if (new_couldassert
== old_couldassert
)
1211 if (PIM_DEBUG_PIM_EVENTS
) {
1212 char src_str
[INET_ADDRSTRLEN
];
1213 char grp_str
[INET_ADDRSTRLEN
];
1214 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1215 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1216 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1217 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1218 ch
->interface
->name
, old_couldassert
,
1222 if (new_couldassert
) {
1223 /* CouldAssert(S,G,I) switched from false to true */
1224 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1226 /* CouldAssert(S,G,I) switched from true to false */
1227 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1229 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1230 assert_action_a4(ch
);
1234 pim_ifchannel_update_my_assert_metric(ch
);
1238 my_assert_metric may be affected by:
1241 pim_ifp->primary_address
1242 rpf->source_nexthop.mrib_metric_preference;
1243 rpf->source_nexthop.mrib_route_metric;
1245 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1247 struct pim_assert_metric my_metric_new
=
1248 pim_macro_ch_my_assert_metric_eval(ch
);
1250 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1253 if (PIM_DEBUG_PIM_EVENTS
) {
1254 char src_str
[INET_ADDRSTRLEN
];
1255 char grp_str
[INET_ADDRSTRLEN
];
1256 char old_addr_str
[INET_ADDRSTRLEN
];
1257 char new_addr_str
[INET_ADDRSTRLEN
];
1258 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1259 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1260 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1261 old_addr_str
, sizeof(old_addr_str
));
1262 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1263 new_addr_str
, sizeof(new_addr_str
));
1265 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1266 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1267 ch
->interface
->name
,
1268 ch
->ifassert_my_metric
.rpt_bit_flag
,
1269 ch
->ifassert_my_metric
.metric_preference
,
1270 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1271 my_metric_new
.rpt_bit_flag
,
1272 my_metric_new
.metric_preference
,
1273 my_metric_new
.route_metric
, new_addr_str
);
1276 ch
->ifassert_my_metric
= my_metric_new
;
1278 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1279 &ch
->ifassert_winner_metric
)) {
1280 assert_action_a5(ch
);
1284 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1286 int old_atd
= PIM_FORCE_BOOLEAN(
1287 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1289 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1291 if (new_atd
== old_atd
)
1294 if (PIM_DEBUG_PIM_EVENTS
) {
1295 char src_str
[INET_ADDRSTRLEN
];
1296 char grp_str
[INET_ADDRSTRLEN
];
1297 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1298 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1300 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1301 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1302 ch
->interface
->name
, old_atd
, new_atd
);
1306 /* AssertTrackingDesired(S,G,I) switched from false to true */
1307 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1309 /* AssertTrackingDesired(S,G,I) switched from true to false */
1310 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1312 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1313 assert_action_a5(ch
);
1319 * If we have a new pim interface, check to
1320 * see if any of the pre-existing channels have
1321 * their upstream out that way and turn on forwarding
1322 * for that ifchannel then.
1324 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1326 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1327 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1328 struct interface
*ifp
;
1330 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1331 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1332 struct pim_ifchannel
*ch
;
1337 if (new_pim_ifp
== loop_pim_ifp
)
1340 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1341 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1342 struct pim_upstream
*up
= ch
->upstream
;
1343 if ((!up
->channel_oil
)
1344 && (up
->rpf
.source_nexthop
1345 .interface
== new_ifp
))
1346 pim_forward_start(ch
);
1353 * Downstream per-interface (S,G,rpt) state machine
1354 * states that we need to move (S,G,rpt) items
1355 * into different states at the start of the
1356 * reception of a *,G join as well, when
1357 * we get End of Message
1359 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1362 bool send_upstream_starg
= false;
1363 struct pim_ifchannel
*child
;
1364 struct listnode
*ch_node
, *nch_node
;
1365 struct pim_instance
*pim
=
1366 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1367 struct pim_upstream
*starup
= ch
->upstream
;
1369 if (PIM_DEBUG_PIM_TRACE
)
1371 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1372 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1373 ch
->sg_str
, eom
, join
);
1377 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1378 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1381 switch (child
->ifjoin_state
) {
1382 case PIM_IFJOIN_NOINFO
:
1383 case PIM_IFJOIN_JOIN
:
1385 case PIM_IFJOIN_PRUNE
:
1387 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1389 case PIM_IFJOIN_PRUNE_PENDING
:
1391 child
->ifjoin_state
=
1392 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1394 case PIM_IFJOIN_PRUNE_TMP
:
1395 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1399 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1400 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1401 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1403 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1404 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1406 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1407 (!pim_upstream_empty_inherited_olist(
1408 child
->upstream
))) {
1409 pim_channel_add_oif(
1410 child
->upstream
->channel_oil
,
1411 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1412 pim_upstream_switch(pim
, child
->upstream
,
1413 PIM_UPSTREAM_JOINED
);
1414 pim_jp_agg_single_upstream_send(
1415 &child
->upstream
->rpf
, child
->upstream
,
1418 send_upstream_starg
= true;
1420 delete_on_noinfo(child
);
1425 if (send_upstream_starg
)
1426 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1429 unsigned int pim_ifchannel_hash_key(const void *arg
)
1431 const struct pim_ifchannel
*ch
= arg
;
1433 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);