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
;
616 ifjoin_to_noinfo(ch
, true);
617 /* ch may have been deleted */
622 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
624 struct pim_ifchannel
*ch
;
625 int send_prune_echo
; /* boolean */
626 struct interface
*ifp
;
627 struct pim_interface
*pim_ifp
;
633 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
634 __PRETTY_FUNCTION__
, pim_str_sg_dump(&ch
->sg
),
635 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
637 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
640 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
641 /* Send PruneEcho(S,G) ? */
643 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
645 if (send_prune_echo
) {
648 rpf
.source_nexthop
.interface
= ifp
;
649 rpf
.rpf_addr
.u
.prefix4
=
650 pim_ifp
->primary_address
;
651 pim_jp_agg_single_upstream_send(
652 &rpf
, ch
->upstream
, 0);
655 ifjoin_to_noinfo(ch
, true);
657 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
658 * message on RP path upon prune timer expiry.
660 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
662 struct pim_upstream
*parent
=
663 ch
->upstream
->parent
;
665 pim_upstream_update_join_desired(pim_ifp
->pim
,
668 pim_jp_agg_single_upstream_send(&parent
->rpf
,
672 /* from here ch may have been deleted */
678 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
679 struct in_addr upstream
, struct prefix_sg
*sg
,
680 uint8_t source_flags
, int holdtime
)
682 struct pim_upstream
*up
;
683 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
685 /* Upstream (S,G) in Joined state ? */
686 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
689 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
692 /* Upstream (S,G) in Joined state */
694 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
695 /* RPF'(S,G) not found */
696 zlog_warn("%s %s: RPF'%s not found", __FILE__
,
697 __PRETTY_FUNCTION__
, up
->sg_str
);
701 /* upstream directed to RPF'(S,G) ? */
702 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
703 char up_str
[INET_ADDRSTRLEN
];
704 char rpf_str
[PREFIX_STRLEN
];
705 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
706 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
709 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
710 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
, up_str
,
711 rpf_str
, recv_ifp
->name
);
714 /* upstream directed to RPF'(S,G) */
717 /* Join(S,G) to RPF'(S,G) */
718 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
723 /* Prune to RPF'(S,G) */
725 if (source_flags
& PIM_RPT_BIT_MASK
) {
726 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
727 /* Prune(*,G) to RPF'(S,G) */
728 pim_upstream_join_timer_decrease_to_t_override(
733 /* Prune(S,G,rpt) to RPF'(S,G) */
734 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
739 /* Prune(S,G) to RPF'(S,G) */
740 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
743 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
744 struct in_addr upstream
, struct prefix_sg
*sg
,
745 uint8_t source_flags
, uint16_t holdtime
)
747 struct pim_interface
*recv_pim_ifp
;
748 int is_local
; /* boolean */
750 recv_pim_ifp
= recv_ifp
->info
;
751 zassert(recv_pim_ifp
);
753 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
758 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
759 char up_str
[INET_ADDRSTRLEN
];
760 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
761 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
762 __PRETTY_FUNCTION__
, is_join
? "join" : "prune",
763 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
767 * Since recv upstream addr was not directed to our primary
768 * address, check if we should react to it in any way.
770 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
773 return 1; /* non-local */
776 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
777 struct in_addr upstream
, struct prefix_sg
*sg
,
778 uint8_t source_flags
, uint16_t holdtime
)
780 struct pim_interface
*pim_ifp
;
781 struct pim_ifchannel
*ch
;
783 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
788 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
789 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
794 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
796 Transitions from "I am Assert Loser" State
798 Receive Join(S,G) on Interface I
800 We receive a Join(S,G) that has the Upstream Neighbor Address
801 field set to my primary IP address on interface I. The action is
802 to transition to NoInfo state, delete this (S,G) assert state
803 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
806 Notice: The nonlocal_upstream() test above ensures the upstream
807 address of the join message is our primary address.
809 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
810 char neigh_str
[INET_ADDRSTRLEN
];
811 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
813 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
814 __PRETTY_FUNCTION__
, ch
->sg_str
, neigh_str
,
817 assert_action_a5(ch
);
823 switch (ch
->ifjoin_state
) {
824 case PIM_IFJOIN_NOINFO
:
825 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
827 if (pim_macro_chisin_oiflist(ch
)) {
828 pim_upstream_inherited_olist(pim_ifp
->pim
,
830 pim_forward_start(ch
);
833 * If we are going to be a LHR, we need to note it
835 if (ch
->upstream
->parent
&& (ch
->upstream
->parent
->flags
836 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
837 && !(ch
->upstream
->flags
838 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
839 pim_upstream_ref(ch
->upstream
,
840 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
841 __PRETTY_FUNCTION__
);
842 pim_upstream_keep_alive_timer_start(
843 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
846 case PIM_IFJOIN_JOIN
:
847 zassert(!ch
->t_ifjoin_prune_pending_timer
);
850 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
852 previously received join message with holdtime=0xFFFF.
854 if (ch
->t_ifjoin_expiry_timer
) {
855 unsigned long remain
= thread_timer_remain_second(
856 ch
->t_ifjoin_expiry_timer
);
857 if (remain
> holdtime
) {
859 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
862 Transitions from Join State
864 The (S,G) downstream state machine on
865 interface I remains in
866 Join state, and the Expiry Timer (ET) is
868 maximum of its current value and the HoldTime
870 triggering Join/Prune message.
872 Conclusion: Do not change the ET if the
874 higher than the received join holdtime.
879 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
881 case PIM_IFJOIN_PRUNE
:
882 if (source_flags
& PIM_ENCODE_RPT_BIT
)
883 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
887 * We have received a S,G join and we are in
888 * S,G RPT Prune state. Which means we need
889 * to transition to Join state and setup
890 * state as appropriate.
892 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
894 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
895 if (pim_upstream_evaluate_join_desired(pim_ifp
->pim
,
897 pim_channel_add_oif(ch
->upstream
->channel_oil
,
899 PIM_OIF_FLAG_PROTO_PIM
);
900 pim_upstream_update_join_desired(pim_ifp
->pim
,
905 case PIM_IFJOIN_PRUNE_PENDING
:
906 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
907 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
908 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
909 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
912 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
915 case PIM_IFJOIN_PRUNE_TMP
:
917 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
921 if (holdtime
!= 0xFFFF) {
922 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
923 holdtime
, &ch
->t_ifjoin_expiry_timer
);
927 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
928 struct prefix_sg
*sg
, uint8_t source_flags
,
931 struct pim_ifchannel
*ch
;
932 struct pim_interface
*pim_ifp
;
933 int jp_override_interval_msec
;
935 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
940 ch
= pim_ifchannel_find(ifp
, sg
);
941 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
944 "%s: Received prune with no relevant ifchannel %s%s state: %d",
945 __PRETTY_FUNCTION__
, ifp
->name
,
946 pim_str_sg_dump(sg
), source_flags
);
950 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
951 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
957 switch (ch
->ifjoin_state
) {
958 case PIM_IFJOIN_NOINFO
:
959 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
960 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
961 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
963 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
964 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
965 jp_override_interval_msec
=
966 pim_if_jp_override_interval_msec(ifp
);
968 jp_override_interval_msec
=
969 0; /* schedule to expire immediately */
970 /* If we called ifjoin_prune() directly instead, care
972 be taken not to use "ch" afterwards since it would be
975 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
976 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
977 thread_add_timer_msec(
978 router
->master
, on_ifjoin_prune_pending_timer
,
979 ch
, jp_override_interval_msec
,
980 &ch
->t_ifjoin_prune_pending_timer
);
981 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
983 &ch
->t_ifjoin_expiry_timer
);
984 pim_upstream_update_join_desired(pim_ifp
->pim
,
988 case PIM_IFJOIN_PRUNE_PENDING
:
991 case PIM_IFJOIN_JOIN
:
992 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
994 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__
, ch
,
995 PIM_IFJOIN_PRUNE_PENDING
);
997 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
998 jp_override_interval_msec
=
999 pim_if_jp_override_interval_msec(ifp
);
1001 jp_override_interval_msec
=
1002 0; /* schedule to expire immediately */
1003 /* If we called ifjoin_prune() directly instead, care should
1004 be taken not to use "ch" afterwards since it would be
1006 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1007 thread_add_timer_msec(router
->master
,
1008 on_ifjoin_prune_pending_timer
, ch
,
1009 jp_override_interval_msec
,
1010 &ch
->t_ifjoin_prune_pending_timer
);
1012 case PIM_IFJOIN_PRUNE
:
1013 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1014 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1015 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1017 &ch
->t_ifjoin_expiry_timer
);
1020 case PIM_IFJOIN_PRUNE_TMP
:
1021 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1022 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1023 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1024 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1026 &ch
->t_ifjoin_expiry_timer
);
1029 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1030 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1031 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1032 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1033 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1035 &ch
->t_ifjoin_expiry_timer
);
1041 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1042 struct prefix_sg
*sg
)
1044 struct pim_ifchannel
*ch
, *starch
;
1045 struct pim_interface
*pim_ifp
;
1046 struct pim_instance
*pim
;
1048 /* PIM enabled on interface? */
1049 pim_ifp
= ifp
->info
;
1051 if (PIM_DEBUG_EVENTS
)
1052 zlog_debug("%s:%s Expected pim interface setup for %s",
1053 __PRETTY_FUNCTION__
,
1054 pim_str_sg_dump(sg
), ifp
->name
);
1058 if (!PIM_IF_TEST_PIM(pim_ifp
->options
)) {
1059 if (PIM_DEBUG_EVENTS
)
1060 zlog_debug("%s:%s PIM is not configured on this interface %s",
1061 __PRETTY_FUNCTION__
,
1062 pim_str_sg_dump(sg
), ifp
->name
);
1068 /* skip (*,G) ch creation if G is of type SSM */
1069 if (sg
->src
.s_addr
== INADDR_ANY
) {
1070 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1071 if (PIM_DEBUG_PIM_EVENTS
)
1073 "%s: local membership (S,G)=%s ignored as group is SSM",
1074 __PRETTY_FUNCTION__
,
1075 pim_str_sg_dump(sg
));
1080 ch
= pim_ifchannel_add(ifp
, sg
, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
);
1082 if (PIM_DEBUG_EVENTS
)
1083 zlog_debug("%s:%s Unable to add ifchannel",
1084 __PRETTY_FUNCTION__
,
1085 pim_str_sg_dump(sg
));
1089 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1091 if (sg
->src
.s_addr
== INADDR_ANY
) {
1092 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1093 struct pim_upstream
*child
;
1094 struct listnode
*up_node
;
1098 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1099 if (PIM_DEBUG_EVENTS
)
1100 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1101 __FILE__
, __PRETTY_FUNCTION__
,
1102 child
->sg_str
, ifp
->name
,
1105 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1106 if (pim_upstream_evaluate_join_desired_interface(
1107 child
, ch
, starch
)) {
1108 pim_channel_add_oif(child
->channel_oil
, ifp
,
1109 PIM_OIF_FLAG_PROTO_STAR
);
1110 pim_upstream_switch(pim
, child
,
1111 PIM_UPSTREAM_JOINED
);
1115 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1116 if (pim
->spt
.plist
) {
1117 struct prefix_list
*plist
= prefix_list_lookup(
1118 AFI_IP
, pim
->spt
.plist
);
1121 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1122 g
.u
.prefix4
= up
->sg
.grp
;
1124 if (prefix_list_apply(plist
, &g
)
1126 pim_channel_add_oif(
1127 up
->channel_oil
, pim
->regiface
,
1128 PIM_OIF_FLAG_PROTO_IGMP
);
1132 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1133 PIM_OIF_FLAG_PROTO_IGMP
);
1139 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1140 struct prefix_sg
*sg
)
1142 struct pim_ifchannel
*starch
, *ch
, *orig
;
1143 struct pim_interface
*pim_ifp
;
1145 /* PIM enabled on interface? */
1146 pim_ifp
= ifp
->info
;
1149 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1152 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1155 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1157 if (sg
->src
.s_addr
== INADDR_ANY
) {
1158 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1159 struct pim_upstream
*child
;
1160 struct listnode
*up_node
, *up_nnode
;
1164 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1165 struct channel_oil
*c_oil
= child
->channel_oil
;
1166 struct pim_ifchannel
*chchannel
=
1167 pim_ifchannel_find(ifp
, &child
->sg
);
1169 pim_ifp
= ifp
->info
;
1171 if (PIM_DEBUG_EVENTS
)
1172 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1173 __FILE__
, __PRETTY_FUNCTION__
,
1174 up
->sg_str
, ifp
->name
,
1177 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1179 && !pim_upstream_evaluate_join_desired_interface(
1181 pim_channel_del_oif(c_oil
, ifp
,
1182 PIM_OIF_FLAG_PROTO_STAR
);
1185 * If the S,G has no if channel and the c_oil still
1186 * has output here then the *,G was supplying the
1188 * if channel. So remove it.
1190 if (!chchannel
&& c_oil
1191 && c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
1192 pim_channel_del_oif(c_oil
, ifp
,
1193 PIM_OIF_FLAG_PROTO_STAR
);
1195 /* Child node removal/ref count-- will happen as part of
1196 * parent' delete_no_info */
1199 delete_on_noinfo(orig
);
1202 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1204 int old_couldassert
=
1205 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1206 int new_couldassert
=
1207 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1209 if (new_couldassert
== old_couldassert
)
1212 if (PIM_DEBUG_PIM_EVENTS
) {
1213 char src_str
[INET_ADDRSTRLEN
];
1214 char grp_str
[INET_ADDRSTRLEN
];
1215 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1216 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1217 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1218 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1219 ch
->interface
->name
, old_couldassert
,
1223 if (new_couldassert
) {
1224 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1225 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1227 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1228 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1230 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1231 assert_action_a4(ch
);
1235 pim_ifchannel_update_my_assert_metric(ch
);
1239 my_assert_metric may be affected by:
1242 pim_ifp->primary_address
1243 rpf->source_nexthop.mrib_metric_preference;
1244 rpf->source_nexthop.mrib_route_metric;
1246 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1248 struct pim_assert_metric my_metric_new
=
1249 pim_macro_ch_my_assert_metric_eval(ch
);
1251 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1254 if (PIM_DEBUG_PIM_EVENTS
) {
1255 char src_str
[INET_ADDRSTRLEN
];
1256 char grp_str
[INET_ADDRSTRLEN
];
1257 char old_addr_str
[INET_ADDRSTRLEN
];
1258 char new_addr_str
[INET_ADDRSTRLEN
];
1259 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1260 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1261 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1262 old_addr_str
, sizeof(old_addr_str
));
1263 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1264 new_addr_str
, sizeof(new_addr_str
));
1266 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1267 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1268 ch
->interface
->name
,
1269 ch
->ifassert_my_metric
.rpt_bit_flag
,
1270 ch
->ifassert_my_metric
.metric_preference
,
1271 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1272 my_metric_new
.rpt_bit_flag
,
1273 my_metric_new
.metric_preference
,
1274 my_metric_new
.route_metric
, new_addr_str
);
1277 ch
->ifassert_my_metric
= my_metric_new
;
1279 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1280 &ch
->ifassert_winner_metric
)) {
1281 assert_action_a5(ch
);
1285 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1287 int old_atd
= PIM_FORCE_BOOLEAN(
1288 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1290 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1292 if (new_atd
== old_atd
)
1295 if (PIM_DEBUG_PIM_EVENTS
) {
1296 char src_str
[INET_ADDRSTRLEN
];
1297 char grp_str
[INET_ADDRSTRLEN
];
1298 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1299 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1301 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1302 __PRETTY_FUNCTION__
, src_str
, grp_str
,
1303 ch
->interface
->name
, old_atd
, new_atd
);
1307 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1308 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1310 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1311 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1313 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1314 assert_action_a5(ch
);
1320 * If we have a new pim interface, check to
1321 * see if any of the pre-existing channels have
1322 * their upstream out that way and turn on forwarding
1323 * for that ifchannel then.
1325 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1327 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1328 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1329 struct interface
*ifp
;
1331 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1332 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1333 struct pim_ifchannel
*ch
;
1338 if (new_pim_ifp
== loop_pim_ifp
)
1341 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1342 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1343 struct pim_upstream
*up
= ch
->upstream
;
1344 if ((!up
->channel_oil
)
1345 && (up
->rpf
.source_nexthop
1346 .interface
== new_ifp
))
1347 pim_forward_start(ch
);
1354 * Downstream per-interface (S,G,rpt) state machine
1355 * states that we need to move (S,G,rpt) items
1356 * into different states at the start of the
1357 * reception of a *,G join as well, when
1358 * we get End of Message
1360 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1363 bool send_upstream_starg
= false;
1364 struct pim_ifchannel
*child
;
1365 struct listnode
*ch_node
, *nch_node
;
1366 struct pim_instance
*pim
=
1367 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1368 struct pim_upstream
*starup
= ch
->upstream
;
1370 if (PIM_DEBUG_PIM_TRACE
)
1372 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__
,
1373 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1374 ch
->sg_str
, eom
, join
);
1378 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1379 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1382 switch (child
->ifjoin_state
) {
1383 case PIM_IFJOIN_NOINFO
:
1384 case PIM_IFJOIN_JOIN
:
1386 case PIM_IFJOIN_PRUNE
:
1388 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1390 case PIM_IFJOIN_PRUNE_PENDING
:
1392 child
->ifjoin_state
=
1393 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1395 case PIM_IFJOIN_PRUNE_TMP
:
1396 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1400 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1401 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1402 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1404 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1405 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1407 if (I_am_RP(pim
, child
->sg
.grp
)) {
1408 pim_channel_add_oif(
1409 child
->upstream
->channel_oil
,
1410 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
1411 pim_upstream_switch(pim
, child
->upstream
,
1412 PIM_UPSTREAM_JOINED
);
1413 pim_jp_agg_single_upstream_send(
1414 &child
->upstream
->rpf
, child
->upstream
,
1417 send_upstream_starg
= true;
1419 delete_on_noinfo(child
);
1424 if (send_upstream_starg
)
1425 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1428 unsigned int pim_ifchannel_hash_key(const void *arg
)
1430 const struct pim_ifchannel
*ch
= arg
;
1432 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);