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
34 #include "pim_zebra.h"
35 #include "pim_iface.h"
41 #include "pim_zlookup.h"
42 #include "pim_ifchannel.h"
44 #include "pim_igmpv3.h"
45 #include "pim_jp_agg.h"
48 #include "pim_vxlan.h"
51 #undef PIM_DEBUG_IFADDR_DUMP
52 #define PIM_DEBUG_IFADDR_DUMP
54 struct zclient
*zclient
;
57 /* Router-id update message from zebra. */
58 static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS
)
60 struct prefix router_id
;
62 zebra_router_id_update_read(zclient
->ibuf
, &router_id
);
67 static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS
)
69 struct interface
*ifp
;
72 ifp
= zebra_interface_vrf_update_read(zclient
->ibuf
, vrf_id
,
78 zlog_debug("%s: %s updating from %u to %u", __func__
, ifp
->name
,
81 if_update_to_new_vrf(ifp
, new_vrf_id
);
86 #ifdef PIM_DEBUG_IFADDR_DUMP
87 static void dump_if_address(struct interface
*ifp
)
89 struct connected
*ifc
;
90 struct listnode
*node
;
92 zlog_debug("%s %s: interface %s addresses:", __FILE__
, __func__
,
95 for (ALL_LIST_ELEMENTS_RO(ifp
->connected
, node
, ifc
)) {
96 struct prefix
*p
= ifc
->address
;
98 if (p
->family
!= AF_INET
)
101 zlog_debug("%s %s: interface %s address %pI4 %s", __FILE__
,
102 __func__
, ifp
->name
, &p
->u
.prefix4
,
103 CHECK_FLAG(ifc
->flags
, ZEBRA_IFA_SECONDARY
)
110 static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS
)
114 struct pim_interface
*pim_ifp
;
115 struct pim_instance
*pim
;
118 zebra api notifies address adds/dels events by using the same call
119 interface_add_read below, see comments in lib/zclient.c
121 zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
122 will add address to interface list by calling
123 connected_add_by_prefix()
125 c
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
129 pim_ifp
= c
->ifp
->info
;
132 if (PIM_DEBUG_ZEBRA
) {
133 zlog_debug("%s: %s(%u) connected IP address %pFX flags %u %s",
134 __func__
, c
->ifp
->name
, vrf_id
, p
, c
->flags
,
135 CHECK_FLAG(c
->flags
, ZEBRA_IFA_SECONDARY
)
139 #ifdef PIM_DEBUG_IFADDR_DUMP
140 dump_if_address(c
->ifp
);
144 if (p
->family
!= PIM_AF
)
145 SET_FLAG(c
->flags
, ZEBRA_IFA_SECONDARY
);
146 else if (!CHECK_FLAG(c
->flags
, ZEBRA_IFA_SECONDARY
)) {
147 /* trying to add primary address? */
148 pim_addr primary_addr
= pim_find_primary_addr(c
->ifp
);
149 pim_addr addr
= pim_addr_from_prefix(p
);
151 if (pim_addr_cmp(primary_addr
, addr
)) {
154 "%s: %s : forcing secondary flag on %pFX",
155 __func__
, c
->ifp
->name
, p
);
156 SET_FLAG(c
->flags
, ZEBRA_IFA_SECONDARY
);
162 pim
= pim_get_pim_instance(vrf_id
);
165 pim_rp_check_on_if_add(pim_ifp
);
168 if (if_is_loopback(c
->ifp
)) {
169 struct vrf
*vrf
= vrf_lookup_by_id(vrf_id
);
170 struct interface
*ifp
;
172 FOR_ALL_INTERFACES (vrf
, ifp
) {
173 if (!if_is_loopback(ifp
) && if_is_operative(ifp
))
174 pim_if_addr_add_all(ifp
);
181 static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS
)
185 struct vrf
*vrf
= vrf_lookup_by_id(vrf_id
);
186 struct pim_instance
*pim
;
193 zebra api notifies address adds/dels events by using the same call
194 interface_add_read below, see comments in lib/zclient.c
196 zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
197 will remove address from interface list by calling
198 connected_delete_by_prefix()
200 c
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
205 if (p
->family
== AF_INET
) {
206 if (PIM_DEBUG_ZEBRA
) {
208 "%s: %s(%u) disconnected IP address %pFX flags %u %s",
209 __func__
, c
->ifp
->name
, vrf_id
, p
, c
->flags
,
210 CHECK_FLAG(c
->flags
, ZEBRA_IFA_SECONDARY
)
214 #ifdef PIM_DEBUG_IFADDR_DUMP
215 dump_if_address(c
->ifp
);
219 pim_if_addr_del(c
, 0);
221 pim_i_am_rp_re_evaluate(pim
);
228 void pim_zebra_update_all_interfaces(struct pim_instance
*pim
)
230 struct interface
*ifp
;
232 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
233 struct pim_interface
*pim_ifp
= ifp
->info
;
234 struct pim_iface_upstream_switch
*us
;
235 struct listnode
*node
;
240 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->upstream_switch_list
, node
,
244 rpf
.source_nexthop
.interface
= ifp
;
245 rpf
.rpf_addr
.u
.prefix4
= us
->address
;
246 pim_joinprune_send(&rpf
, us
->us
);
247 pim_jp_agg_clear_group(us
->us
);
252 void pim_zebra_upstream_rpf_changed(struct pim_instance
*pim
,
253 struct pim_upstream
*up
,
256 if (old
->source_nexthop
.interface
) {
257 struct pim_neighbor
*nbr
;
259 nbr
= pim_neighbor_find_prefix(old
->source_nexthop
.interface
,
262 pim_jp_agg_remove_group(nbr
->upstream_jp_agg
, up
, nbr
);
265 * We have detected a case where we might need
266 * to rescan the inherited o_list so do it.
268 if (up
->channel_oil
->oil_inherited_rescan
) {
269 pim_upstream_inherited_olist_decide(pim
, up
);
270 up
->channel_oil
->oil_inherited_rescan
= 0;
273 if (up
->join_state
== PIM_UPSTREAM_JOINED
) {
275 * If we come up real fast we can be here
276 * where the mroute has not been installed
279 if (!up
->channel_oil
->installed
)
280 pim_upstream_mroute_add(up
->channel_oil
,
284 * RFC 4601: 4.5.7. Sending (S,G)
285 * Join/Prune Messages
287 * Transitions from Joined State
289 * RPF'(S,G) changes not due to an Assert
291 * The upstream (S,G) state machine remains
292 * in Joined state. Send Join(S,G) to the new
293 * upstream neighbor, which is the new value
294 * of RPF'(S,G). Send Prune(S,G) to the old
295 * upstream neighbor, which is the old value
296 * of RPF'(S,G). Set the Join Timer (JT) to
297 * expire after t_periodic seconds.
299 pim_jp_agg_switch_interface(old
, &up
->rpf
, up
);
301 pim_upstream_join_timer_restart(up
, old
);
302 } /* up->join_state == PIM_UPSTREAM_JOINED */
307 * We have detected a case where we might need
308 * to rescan the inherited o_list so do it.
310 if (up
->channel_oil
->oil_inherited_rescan
) {
311 pim_upstream_inherited_olist_decide(pim
, up
);
312 up
->channel_oil
->oil_inherited_rescan
= 0;
315 if (up
->join_state
== PIM_UPSTREAM_JOINED
)
316 pim_jp_agg_switch_interface(old
, &up
->rpf
, up
);
318 if (!up
->channel_oil
->installed
)
319 pim_upstream_mroute_add(up
->channel_oil
, __func__
);
322 /* FIXME can join_desired actually be changed by pim_rpf_update()
323 * returning PIM_RPF_CHANGED ?
325 pim_upstream_update_join_desired(pim
, up
);
328 static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS
)
331 struct pim_instance
*pim
;
335 pim
= pim_get_pim_instance(vrf_id
);
341 prefixlen
= stream_getl(s
);
342 stream_get(&sg
.src
, s
, prefixlen
);
343 stream_get(&sg
.grp
, s
, prefixlen
);
346 zlog_debug("%u:recv SG %s %pSG", vrf_id
,
347 (cmd
== ZEBRA_VXLAN_SG_ADD
) ? "add" : "del", &sg
);
349 if (cmd
== ZEBRA_VXLAN_SG_ADD
)
350 pim_vxlan_sg_add(pim
, &sg
);
352 pim_vxlan_sg_del(pim
, &sg
);
357 static void pim_zebra_vxlan_replay(void)
359 struct stream
*s
= NULL
;
362 if (!zclient
|| zclient
->sock
< 0)
368 zclient_create_header(s
, ZEBRA_VXLAN_SG_REPLAY
, VRF_DEFAULT
);
369 stream_putw_at(s
, 0, stream_get_endp(s
));
371 zclient_send_message(zclient
);
374 void pim_scan_oil(struct pim_instance
*pim
)
376 struct channel_oil
*c_oil
;
378 pim
->scan_oil_last
= pim_time_monotonic_sec();
379 ++pim
->scan_oil_events
;
381 frr_each (rb_pim_oil
, &pim
->channel_oil_head
, c_oil
)
382 pim_upstream_mroute_iif_update(c_oil
, __func__
);
385 static int on_rpf_cache_refresh(struct thread
*t
)
387 struct pim_instance
*pim
= THREAD_ARG(t
);
389 /* update kernel multicast forwarding cache (MFC) */
392 pim
->rpf_cache_refresh_last
= pim_time_monotonic_sec();
393 ++pim
->rpf_cache_refresh_events
;
395 // It is called as part of pim_neighbor_add
400 void sched_rpf_cache_refresh(struct pim_instance
*pim
)
402 ++pim
->rpf_cache_refresh_requests
;
404 pim_rpf_set_refresh_time(pim
);
406 if (pim
->rpf_cache_refresher
) {
407 /* Refresh timer is already running */
411 /* Start refresh timer */
413 if (PIM_DEBUG_ZEBRA
) {
414 zlog_debug("%s: triggering %ld msec timer", __func__
,
415 router
->rpf_cache_refresh_delay_msec
);
418 thread_add_timer_msec(router
->master
, on_rpf_cache_refresh
, pim
,
419 router
->rpf_cache_refresh_delay_msec
,
420 &pim
->rpf_cache_refresher
);
423 static void pim_zebra_connected(struct zclient
*zclient
)
425 /* Send the client registration */
426 bfd_client_sendmsg(zclient
, ZEBRA_BFD_CLIENT_REGISTER
, router
->vrf_id
);
428 zclient_send_reg_requests(zclient
, router
->vrf_id
);
430 /* request for VxLAN BUM group addresses */
431 pim_zebra_vxlan_replay();
434 static void pim_zebra_capabilities(struct zclient_capabilities
*cap
)
436 router
->mlag_role
= cap
->role
;
439 static zclient_handler
*const pim_handlers
[] = {
440 [ZEBRA_ROUTER_ID_UPDATE
] = pim_router_id_update_zebra
,
441 [ZEBRA_INTERFACE_ADDRESS_ADD
] = pim_zebra_if_address_add
,
442 [ZEBRA_INTERFACE_ADDRESS_DELETE
] = pim_zebra_if_address_del
,
443 [ZEBRA_INTERFACE_VRF_UPDATE
] = pim_zebra_interface_vrf_update
,
444 [ZEBRA_NEXTHOP_UPDATE
] = pim_parse_nexthop_update
,
446 [ZEBRA_VXLAN_SG_ADD
] = pim_zebra_vxlan_sg_proc
,
447 [ZEBRA_VXLAN_SG_DEL
] = pim_zebra_vxlan_sg_proc
,
449 [ZEBRA_MLAG_PROCESS_UP
] = pim_zebra_mlag_process_up
,
450 [ZEBRA_MLAG_PROCESS_DOWN
] = pim_zebra_mlag_process_down
,
451 [ZEBRA_MLAG_FORWARD_MSG
] = pim_zebra_mlag_handle_msg
,
454 void pim_zebra_init(void)
456 /* Socket for receiving updates from Zebra daemon */
457 zclient
= zclient_new(router
->master
, &zclient_options_default
,
458 pim_handlers
, array_size(pim_handlers
));
460 zclient
->zebra_capabilities
= pim_zebra_capabilities
;
461 zclient
->zebra_connected
= pim_zebra_connected
;
463 zclient_init(zclient
, ZEBRA_ROUTE_PIM
, 0, &pimd_privs
);
464 if (PIM_DEBUG_PIM_TRACE
) {
465 zlog_notice("%s: zclient socket initialized", __func__
);
468 zclient_lookup_new();
471 void igmp_anysource_forward_start(struct pim_instance
*pim
,
472 struct gm_group
*group
)
474 struct gm_source
*source
;
475 struct in_addr src_addr
= {.s_addr
= 0};
476 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
477 assert(group
->group_filtermode_isexcl
);
478 assert(listcount(group
->group_source_list
) < 1);
480 source
= igmp_get_source_by_addr(group
, src_addr
, NULL
);
482 zlog_warn("%s: Failure to create * source", __func__
);
486 igmp_source_forward_start(pim
, source
);
489 void igmp_anysource_forward_stop(struct gm_group
*group
)
491 struct gm_source
*source
;
492 struct in_addr star
= {.s_addr
= 0};
494 source
= igmp_find_source_by_addr(group
, star
);
496 igmp_source_forward_stop(source
);
499 static void igmp_source_forward_reevaluate_one(struct pim_instance
*pim
,
500 struct gm_source
*source
)
503 struct gm_group
*group
= source
->source_group
;
504 struct pim_ifchannel
*ch
;
506 if ((source
->source_addr
.s_addr
!= INADDR_ANY
)
507 || !IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
))
510 memset(&sg
, 0, sizeof(sg
));
511 sg
.src
= source
->source_addr
;
512 sg
.grp
= group
->group_addr
;
514 ch
= pim_ifchannel_find(group
->interface
, &sg
);
515 if (pim_is_grp_ssm(pim
, group
->group_addr
)) {
516 /* If SSM group withdraw local membership */
518 && (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_INCLUDE
)) {
519 if (PIM_DEBUG_PIM_EVENTS
)
520 zlog_debug("local membership del for %pSG as G is now SSM",
522 pim_ifchannel_local_membership_del(group
->interface
,
526 /* If ASM group add local membership */
528 || (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
)) {
529 if (PIM_DEBUG_PIM_EVENTS
)
530 zlog_debug("local membership add for %pSG as G is now ASM",
532 pim_ifchannel_local_membership_add(
533 group
->interface
, &sg
, false /*is_vxlan*/);
538 void igmp_source_forward_reevaluate_all(struct pim_instance
*pim
)
540 struct interface
*ifp
;
542 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
543 struct pim_interface
*pim_ifp
= ifp
->info
;
544 struct listnode
*grpnode
;
545 struct gm_group
*grp
;
546 struct pim_ifchannel
*ch
, *ch_temp
;
551 /* scan igmp groups */
552 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_group_list
, grpnode
,
554 struct listnode
*srcnode
;
555 struct gm_source
*src
;
557 /* scan group sources */
558 for (ALL_LIST_ELEMENTS_RO(grp
->group_source_list
,
560 igmp_source_forward_reevaluate_one(pim
, src
);
561 } /* scan group sources */
562 } /* scan igmp groups */
564 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
,
566 if (pim_is_grp_ssm(pim
, ch
->sg
.grp
)) {
567 if (pim_addr_is_any(ch
->sg
.src
))
568 pim_ifchannel_delete(ch
);
571 } /* scan interfaces */
574 void igmp_source_forward_start(struct pim_instance
*pim
,
575 struct gm_source
*source
)
577 struct pim_interface
*pim_oif
;
578 struct gm_group
*group
;
581 int input_iface_vif_index
= 0;
583 memset(&sg
, 0, sizeof(sg
));
584 sg
.src
= source
->source_addr
;
585 sg
.grp
= source
->source_group
->group_addr
;
587 if (PIM_DEBUG_IGMP_TRACE
) {
588 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
589 source
->source_group
->interface
->name
,
590 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
593 /* Prevent IGMP interface from installing multicast route multiple
595 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
599 group
= source
->source_group
;
600 pim_oif
= group
->interface
->info
;
602 if (PIM_DEBUG_IGMP_TRACE
) {
603 zlog_debug("%s: multicast not enabled on oif=%s ?",
605 source
->source_group
->interface
->name
);
610 if (!source
->source_channel_oil
) {
612 struct prefix src
, grp
;
613 struct pim_nexthop nexthop
;
614 struct pim_upstream
*up
= NULL
;
616 if (!pim_rp_set_upstream_addr(pim
, &vif_source
,
617 source
->source_addr
, sg
.grp
)) {
618 /*Create a dummy channel oil */
619 source
->source_channel_oil
=
620 pim_channel_oil_add(pim
, &sg
, __func__
);
624 pim_addr_to_prefix(&src
, vif_source
); // RP or Src addr
625 pim_addr_to_prefix(&grp
, sg
.grp
);
627 up
= pim_upstream_find(pim
, &sg
);
629 memcpy(&nexthop
, &up
->rpf
.source_nexthop
,
630 sizeof(struct pim_nexthop
));
631 pim_ecmp_nexthop_lookup(pim
, &nexthop
, &src
,
633 if (nexthop
.interface
)
634 input_iface_vif_index
=
635 pim_if_find_vifindex_by_ifindex(
637 nexthop
.interface
->ifindex
);
639 input_iface_vif_index
=
640 pim_ecmp_fib_lookup_if_vif_index(
645 "%s: NHT %pSG vif_source %pPAs vif_index:%d ",
646 __func__
, &sg
, &vif_source
,
647 input_iface_vif_index
);
649 if (input_iface_vif_index
< 1) {
650 if (PIM_DEBUG_IGMP_TRACE
) {
651 char source_str
[INET_ADDRSTRLEN
];
652 pim_inet4_dump("<source?>",
654 source_str
, sizeof(source_str
));
656 "%s %s: could not find input interface for source %s",
657 __FILE__
, __func__
, source_str
);
659 source
->source_channel_oil
=
660 pim_channel_oil_add(pim
, &sg
, __func__
);
665 * Protect IGMP against adding looped MFC
666 * entries created by both source and receiver
667 * attached to the same interface. See TODO
668 * T22. Block only when the intf is non DR
669 * DR must create upstream.
671 if ((input_iface_vif_index
==
672 pim_oif
->mroute_vif_index
) &&
673 !(PIM_I_am_DR(pim_oif
))) {
674 /* ignore request for looped MFC entry
676 if (PIM_DEBUG_IGMP_TRACE
) {
677 zlog_debug("%s: ignoring request for looped MFC entry (S,G)=%pSG: oif=%s vif_index=%d",
682 input_iface_vif_index
);
687 source
->source_channel_oil
=
688 pim_channel_oil_add(pim
, &sg
, __func__
);
689 if (!source
->source_channel_oil
) {
690 if (PIM_DEBUG_IGMP_TRACE
) {
691 zlog_debug("%s %s: could not create OIL for channel (S,G)=%pSG",
701 if (PIM_I_am_DR(pim_oif
) || PIM_I_am_DualActive(pim_oif
)) {
702 result
= pim_channel_add_oif(source
->source_channel_oil
,
704 PIM_OIF_FLAG_PROTO_IGMP
, __func__
);
706 if (PIM_DEBUG_MROUTE
) {
707 zlog_warn("%s: add_oif() failed with return=%d",
713 if (PIM_DEBUG_IGMP_TRACE
)
714 zlog_debug("%s: %pSG was received on %s interface but we are not DR for that interface",
716 group
->interface
->name
);
721 Feed IGMPv3-gathered local membership information into PIM
722 per-interface (S,G) state.
724 if (!pim_ifchannel_local_membership_add(group
->interface
, &sg
,
725 false /*is_vxlan*/)) {
726 if (PIM_DEBUG_MROUTE
)
727 zlog_warn("%s: Failure to add local membership for %pSG",
730 pim_channel_del_oif(source
->source_channel_oil
,
731 group
->interface
, PIM_OIF_FLAG_PROTO_IGMP
,
736 IGMP_SOURCE_DO_FORWARDING(source
->source_flags
);
740 igmp_source_forward_stop: stop fowarding, but keep the source
741 igmp_source_delete: stop fowarding, and delete the source
743 void igmp_source_forward_stop(struct gm_source
*source
)
745 struct gm_group
*group
;
749 memset(&sg
, 0, sizeof(sg
));
750 sg
.src
= source
->source_addr
;
751 sg
.grp
= source
->source_group
->group_addr
;
753 if (PIM_DEBUG_IGMP_TRACE
) {
754 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
755 source
->source_group
->interface
->name
,
756 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
759 /* Prevent IGMP interface from removing multicast route multiple
761 if (!IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
765 group
= source
->source_group
;
768 It appears that in certain circumstances that
769 igmp_source_forward_stop is called when IGMP forwarding
770 was not enabled in oif_flags for this outgoing interface.
771 Possibly because of multiple calls. When that happens, we
772 enter the below if statement and this function returns early
773 which in turn triggers the calling function to assert.
774 Making the call to pim_channel_del_oif and ignoring the return code
775 fixes the issue without ill effect, similar to
776 pim_forward_stop below.
778 result
= pim_channel_del_oif(source
->source_channel_oil
,
779 group
->interface
, PIM_OIF_FLAG_PROTO_IGMP
,
782 if (PIM_DEBUG_IGMP_TRACE
)
784 "%s: pim_channel_del_oif() failed with return=%d",
790 Feed IGMPv3-gathered local membership information into PIM
791 per-interface (S,G) state.
793 pim_ifchannel_local_membership_del(group
->interface
, &sg
);
795 IGMP_SOURCE_DONT_FORWARDING(source
->source_flags
);
798 void pim_forward_start(struct pim_ifchannel
*ch
)
800 struct pim_upstream
*up
= ch
->upstream
;
803 if (PIM_DEBUG_PIM_TRACE
)
804 zlog_debug("%s: (S,G)=%pSG oif=%s (%pI4)", __func__
, &ch
->sg
,
805 ch
->interface
->name
, &up
->upstream_addr
);
807 if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch
->flags
))
808 mask
= PIM_OIF_FLAG_PROTO_IGMP
;
810 if (PIM_IF_FLAG_TEST_PROTO_PIM(ch
->flags
))
811 mask
|= PIM_OIF_FLAG_PROTO_PIM
;
813 pim_channel_add_oif(up
->channel_oil
, ch
->interface
,
817 void pim_forward_stop(struct pim_ifchannel
*ch
)
819 struct pim_upstream
*up
= ch
->upstream
;
821 if (PIM_DEBUG_PIM_TRACE
) {
822 zlog_debug("%s: (S,G)=%s oif=%s installed: %d",
823 __func__
, ch
->sg_str
, ch
->interface
->name
,
824 up
->channel_oil
->installed
);
828 * If a channel is being removed, check to see if we still need
829 * to inherit the interface. If so make sure it is added in
831 if (pim_upstream_evaluate_join_desired_interface(up
, ch
, ch
->parent
))
832 pim_channel_add_oif(up
->channel_oil
, ch
->interface
,
833 PIM_OIF_FLAG_PROTO_PIM
, __func__
);
835 pim_channel_del_oif(up
->channel_oil
, ch
->interface
,
836 PIM_OIF_FLAG_PROTO_PIM
, __func__
);
839 void pim_zebra_zclient_update(struct vty
*vty
)
841 vty_out(vty
, "Zclient update socket: ");
844 vty_out(vty
, "%d failures=%d\n", zclient
->sock
, zclient
->fail
);
846 vty_out(vty
, "<null zclient>\n");
850 struct zclient
*pim_zebra_zclient_get(void)
858 void pim_zebra_interface_set_master(struct interface
*vrf
,
859 struct interface
*ifp
)
861 zclient_interface_set_master(zclient
, vrf
, ifp
);