mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 11:45:06 +00:00 
			
		
		
		
	In this PR, we are handling the pim_zebra code after rpf_addr modified from prefix to pim_addr. Signed-off-by: sarita patra <saritap@vmware.com>
		
			
				
	
	
		
			558 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			558 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * PIM for Quagga
 | 
						|
 * Copyright (C) 2008  Everton da Silva Marques
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation; either version 2 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but
 | 
						|
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License along
 | 
						|
 * with this program; see the file COPYING; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include "if.h"
 | 
						|
#include "log.h"
 | 
						|
#include "prefix.h"
 | 
						|
#include "zclient.h"
 | 
						|
#include "stream.h"
 | 
						|
#include "network.h"
 | 
						|
#include "vty.h"
 | 
						|
#include "plist.h"
 | 
						|
#include "lib/bfd.h"
 | 
						|
 | 
						|
#include "pimd.h"
 | 
						|
#include "pim_pim.h"
 | 
						|
#include "pim_zebra.h"
 | 
						|
#include "pim_iface.h"
 | 
						|
#include "pim_str.h"
 | 
						|
#include "pim_oil.h"
 | 
						|
#include "pim_rpf.h"
 | 
						|
#include "pim_time.h"
 | 
						|
#include "pim_join.h"
 | 
						|
#include "pim_zlookup.h"
 | 
						|
#include "pim_ifchannel.h"
 | 
						|
#include "pim_rp.h"
 | 
						|
#include "pim_igmpv3.h"
 | 
						|
#include "pim_jp_agg.h"
 | 
						|
#include "pim_nht.h"
 | 
						|
#include "pim_ssm.h"
 | 
						|
#include "pim_vxlan.h"
 | 
						|
#include "pim_mlag.h"
 | 
						|
 | 
						|
#undef PIM_DEBUG_IFADDR_DUMP
 | 
						|
#define PIM_DEBUG_IFADDR_DUMP
 | 
						|
 | 
						|
struct zclient *zclient;
 | 
						|
 | 
						|
 | 
						|
/* Router-id update message from zebra. */
 | 
						|
static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
 | 
						|
{
 | 
						|
	struct prefix router_id;
 | 
						|
 | 
						|
	zebra_router_id_update_read(zclient->ibuf, &router_id);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS)
 | 
						|
{
 | 
						|
	struct interface *ifp;
 | 
						|
	vrf_id_t new_vrf_id;
 | 
						|
 | 
						|
	ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id,
 | 
						|
					      &new_vrf_id);
 | 
						|
	if (!ifp)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (PIM_DEBUG_ZEBRA)
 | 
						|
		zlog_debug("%s: %s updating from %u to %u", __func__, ifp->name,
 | 
						|
			   vrf_id, new_vrf_id);
 | 
						|
 | 
						|
	if_update_to_new_vrf(ifp, new_vrf_id);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef PIM_DEBUG_IFADDR_DUMP
 | 
						|
static void dump_if_address(struct interface *ifp)
 | 
						|
{
 | 
						|
	struct connected *ifc;
 | 
						|
	struct listnode *node;
 | 
						|
 | 
						|
	zlog_debug("%s %s: interface %s addresses:", __FILE__, __func__,
 | 
						|
		   ifp->name);
 | 
						|
 | 
						|
	for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
 | 
						|
		struct prefix *p = ifc->address;
 | 
						|
 | 
						|
		if (p->family != AF_INET)
 | 
						|
			continue;
 | 
						|
 | 
						|
		zlog_debug("%s %s: interface %s address %pI4 %s", __FILE__,
 | 
						|
			   __func__, ifp->name, &p->u.prefix4,
 | 
						|
			   CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)
 | 
						|
				   ? "secondary"
 | 
						|
				   : "primary");
 | 
						|
	}
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS)
 | 
						|
{
 | 
						|
	struct connected *c;
 | 
						|
	struct prefix *p;
 | 
						|
	struct pim_interface *pim_ifp;
 | 
						|
 | 
						|
	/*
 | 
						|
	  zebra api notifies address adds/dels events by using the same call
 | 
						|
	  interface_add_read below, see comments in lib/zclient.c
 | 
						|
 | 
						|
	  zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
 | 
						|
	  will add address to interface list by calling
 | 
						|
	  connected_add_by_prefix()
 | 
						|
	*/
 | 
						|
	c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
 | 
						|
	if (!c)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	pim_ifp = c->ifp->info;
 | 
						|
	p = c->address;
 | 
						|
 | 
						|
	if (PIM_DEBUG_ZEBRA) {
 | 
						|
		zlog_debug("%s: %s(%u) connected IP address %pFX flags %u %s",
 | 
						|
			   __func__, c->ifp->name, vrf_id, p, c->flags,
 | 
						|
			   CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)
 | 
						|
				   ? "secondary"
 | 
						|
				   : "primary");
 | 
						|
 | 
						|
#ifdef PIM_DEBUG_IFADDR_DUMP
 | 
						|
		dump_if_address(c->ifp);
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
#if PIM_IPV == 4
 | 
						|
	if (p->family != PIM_AF)
 | 
						|
		SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
 | 
						|
	else if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) {
 | 
						|
		/* trying to add primary address? */
 | 
						|
		pim_addr primary_addr = pim_find_primary_addr(c->ifp);
 | 
						|
		pim_addr addr = pim_addr_from_prefix(p);
 | 
						|
 | 
						|
		if (pim_addr_cmp(primary_addr, addr)) {
 | 
						|
			if (PIM_DEBUG_ZEBRA)
 | 
						|
				zlog_warn(
 | 
						|
					"%s: %s : forcing secondary flag on %pFX",
 | 
						|
					__func__, c->ifp->name, p);
 | 
						|
			SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
 | 
						|
		}
 | 
						|
	}
 | 
						|
#else /* PIM_IPV != 4 */
 | 
						|
	if (p->family != PIM_AF)
 | 
						|
		return 0;
 | 
						|
#endif
 | 
						|
 | 
						|
	pim_if_addr_add(c);
 | 
						|
	if (pim_ifp) {
 | 
						|
		struct pim_instance *pim;
 | 
						|
 | 
						|
		pim = pim_get_pim_instance(vrf_id);
 | 
						|
		if (!pim) {
 | 
						|
			if (PIM_DEBUG_ZEBRA)
 | 
						|
				zlog_debug("%s: Unable to find pim instance",
 | 
						|
					   __func__);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
 | 
						|
		pim_ifp->pim = pim;
 | 
						|
 | 
						|
		pim_rp_check_on_if_add(pim_ifp);
 | 
						|
	}
 | 
						|
 | 
						|
	if (if_is_loopback(c->ifp)) {
 | 
						|
		struct vrf *vrf = vrf_lookup_by_id(vrf_id);
 | 
						|
		struct interface *ifp;
 | 
						|
 | 
						|
		FOR_ALL_INTERFACES (vrf, ifp) {
 | 
						|
			if (!if_is_loopback(ifp) && if_is_operative(ifp))
 | 
						|
				pim_if_addr_add_all(ifp);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS)
 | 
						|
{
 | 
						|
	struct connected *c;
 | 
						|
	struct prefix *p;
 | 
						|
	struct vrf *vrf = vrf_lookup_by_id(vrf_id);
 | 
						|
 | 
						|
	if (!vrf)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	/*
 | 
						|
	  zebra api notifies address adds/dels events by using the same call
 | 
						|
	  interface_add_read below, see comments in lib/zclient.c
 | 
						|
 | 
						|
	  zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
 | 
						|
	  will remove address from interface list by calling
 | 
						|
	  connected_delete_by_prefix()
 | 
						|
	*/
 | 
						|
	c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
 | 
						|
	if (!c)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	p = c->address;
 | 
						|
 | 
						|
	if (PIM_DEBUG_ZEBRA) {
 | 
						|
		zlog_debug(
 | 
						|
			"%s: %s(%u) disconnected IP address %pFX flags %u %s",
 | 
						|
			__func__, c->ifp->name, vrf_id, p, c->flags,
 | 
						|
			CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)
 | 
						|
				? "secondary"
 | 
						|
				: "primary");
 | 
						|
#ifdef PIM_DEBUG_IFADDR_DUMP
 | 
						|
		dump_if_address(c->ifp);
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	if (p->family == PIM_AF) {
 | 
						|
		struct pim_instance *pim;
 | 
						|
 | 
						|
		pim = vrf->info;
 | 
						|
		pim_if_addr_del(c, 0);
 | 
						|
		pim_rp_setup(pim);
 | 
						|
		pim_i_am_rp_re_evaluate(pim);
 | 
						|
	}
 | 
						|
 | 
						|
	connected_free(&c);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void pim_zebra_update_all_interfaces(struct pim_instance *pim)
 | 
						|
{
 | 
						|
	struct interface *ifp;
 | 
						|
 | 
						|
	FOR_ALL_INTERFACES (pim->vrf, ifp) {
 | 
						|
		struct pim_interface *pim_ifp = ifp->info;
 | 
						|
		struct pim_iface_upstream_switch *us;
 | 
						|
		struct listnode *node;
 | 
						|
 | 
						|
		if (!pim_ifp)
 | 
						|
			continue;
 | 
						|
 | 
						|
		for (ALL_LIST_ELEMENTS_RO(pim_ifp->upstream_switch_list, node,
 | 
						|
					  us)) {
 | 
						|
			struct pim_rpf rpf;
 | 
						|
 | 
						|
			rpf.source_nexthop.interface = ifp;
 | 
						|
			rpf.rpf_addr = us->address;
 | 
						|
			pim_joinprune_send(&rpf, us->us);
 | 
						|
			pim_jp_agg_clear_group(us->us);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void pim_zebra_upstream_rpf_changed(struct pim_instance *pim,
 | 
						|
				    struct pim_upstream *up,
 | 
						|
				    struct pim_rpf *old)
 | 
						|
{
 | 
						|
	if (old->source_nexthop.interface) {
 | 
						|
		struct pim_neighbor *nbr;
 | 
						|
 | 
						|
		nbr = pim_neighbor_find(old->source_nexthop.interface,
 | 
						|
					old->rpf_addr);
 | 
						|
		if (nbr)
 | 
						|
			pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * We have detected a case where we might need
 | 
						|
		 * to rescan the inherited o_list so do it.
 | 
						|
		 */
 | 
						|
		if (up->channel_oil->oil_inherited_rescan) {
 | 
						|
			pim_upstream_inherited_olist_decide(pim, up);
 | 
						|
			up->channel_oil->oil_inherited_rescan = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (up->join_state == PIM_UPSTREAM_JOINED) {
 | 
						|
			/*
 | 
						|
			 * If we come up real fast we can be here
 | 
						|
			 * where the mroute has not been installed
 | 
						|
			 * so install it.
 | 
						|
			 */
 | 
						|
			if (!up->channel_oil->installed)
 | 
						|
				pim_upstream_mroute_add(up->channel_oil,
 | 
						|
							__func__);
 | 
						|
 | 
						|
			/*
 | 
						|
			 * RFC 4601: 4.5.7.  Sending (S,G)
 | 
						|
			 * Join/Prune Messages
 | 
						|
			 *
 | 
						|
			 * Transitions from Joined State
 | 
						|
			 *
 | 
						|
			 * RPF'(S,G) changes not due to an Assert
 | 
						|
			 *
 | 
						|
			 * The upstream (S,G) state machine remains
 | 
						|
			 * in Joined state. Send Join(S,G) to the new
 | 
						|
			 * upstream neighbor, which is the new value
 | 
						|
			 * of RPF'(S,G).  Send Prune(S,G) to the old
 | 
						|
			 * upstream neighbor, which is the old value
 | 
						|
			 * of RPF'(S,G).  Set the Join Timer (JT) to
 | 
						|
			 * expire after t_periodic seconds.
 | 
						|
			 */
 | 
						|
			pim_jp_agg_switch_interface(old, &up->rpf, up);
 | 
						|
 | 
						|
			pim_upstream_join_timer_restart(up, old);
 | 
						|
		} /* up->join_state == PIM_UPSTREAM_JOINED */
 | 
						|
	}
 | 
						|
 | 
						|
	else {
 | 
						|
		/*
 | 
						|
		 * We have detected a case where we might need
 | 
						|
		 * to rescan the inherited o_list so do it.
 | 
						|
		 */
 | 
						|
		if (up->channel_oil->oil_inherited_rescan) {
 | 
						|
			pim_upstream_inherited_olist_decide(pim, up);
 | 
						|
			up->channel_oil->oil_inherited_rescan = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (up->join_state == PIM_UPSTREAM_JOINED)
 | 
						|
			pim_jp_agg_switch_interface(old, &up->rpf, up);
 | 
						|
 | 
						|
		if (!up->channel_oil->installed)
 | 
						|
			pim_upstream_mroute_add(up->channel_oil, __func__);
 | 
						|
	}
 | 
						|
 | 
						|
	/* FIXME can join_desired actually be changed by pim_rpf_update()
 | 
						|
	 * returning PIM_RPF_CHANGED ?
 | 
						|
	 */
 | 
						|
	pim_upstream_update_join_desired(pim, up);
 | 
						|
}
 | 
						|
 | 
						|
__attribute__((unused))
 | 
						|
static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS)
 | 
						|
{
 | 
						|
	struct stream *s;
 | 
						|
	struct pim_instance *pim;
 | 
						|
	pim_sgaddr sg;
 | 
						|
	size_t prefixlen;
 | 
						|
 | 
						|
	pim = pim_get_pim_instance(vrf_id);
 | 
						|
	if (!pim)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	s = zclient->ibuf;
 | 
						|
 | 
						|
	prefixlen = stream_getl(s);
 | 
						|
	stream_get(&sg.src, s, prefixlen);
 | 
						|
	stream_get(&sg.grp, s, prefixlen);
 | 
						|
 | 
						|
	if (PIM_DEBUG_ZEBRA)
 | 
						|
		zlog_debug("%u:recv SG %s %pSG", vrf_id,
 | 
						|
			   (cmd == ZEBRA_VXLAN_SG_ADD) ? "add" : "del", &sg);
 | 
						|
 | 
						|
	if (cmd == ZEBRA_VXLAN_SG_ADD)
 | 
						|
		pim_vxlan_sg_add(pim, &sg);
 | 
						|
	else
 | 
						|
		pim_vxlan_sg_del(pim, &sg);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
__attribute__((unused))
 | 
						|
static void pim_zebra_vxlan_replay(void)
 | 
						|
{
 | 
						|
	struct stream *s = NULL;
 | 
						|
 | 
						|
	/* Check socket. */
 | 
						|
	if (!zclient || zclient->sock < 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	s = zclient->obuf;
 | 
						|
	stream_reset(s);
 | 
						|
 | 
						|
	zclient_create_header(s, ZEBRA_VXLAN_SG_REPLAY, VRF_DEFAULT);
 | 
						|
	stream_putw_at(s, 0, stream_get_endp(s));
 | 
						|
 | 
						|
	zclient_send_message(zclient);
 | 
						|
}
 | 
						|
 | 
						|
void pim_scan_oil(struct pim_instance *pim)
 | 
						|
{
 | 
						|
	struct channel_oil *c_oil;
 | 
						|
 | 
						|
	pim->scan_oil_last = pim_time_monotonic_sec();
 | 
						|
	++pim->scan_oil_events;
 | 
						|
 | 
						|
	frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil)
 | 
						|
		pim_upstream_mroute_iif_update(c_oil, __func__);
 | 
						|
}
 | 
						|
 | 
						|
static void on_rpf_cache_refresh(struct thread *t)
 | 
						|
{
 | 
						|
	struct pim_instance *pim = THREAD_ARG(t);
 | 
						|
 | 
						|
	/* update kernel multicast forwarding cache (MFC) */
 | 
						|
	pim_scan_oil(pim);
 | 
						|
 | 
						|
	pim->rpf_cache_refresh_last = pim_time_monotonic_sec();
 | 
						|
	++pim->rpf_cache_refresh_events;
 | 
						|
 | 
						|
	// It is called as part of pim_neighbor_add
 | 
						|
	// pim_rp_setup ();
 | 
						|
}
 | 
						|
 | 
						|
void sched_rpf_cache_refresh(struct pim_instance *pim)
 | 
						|
{
 | 
						|
	++pim->rpf_cache_refresh_requests;
 | 
						|
 | 
						|
	pim_rpf_set_refresh_time(pim);
 | 
						|
 | 
						|
	if (pim->rpf_cache_refresher) {
 | 
						|
		/* Refresh timer is already running */
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Start refresh timer */
 | 
						|
 | 
						|
	if (PIM_DEBUG_ZEBRA) {
 | 
						|
		zlog_debug("%s: triggering %ld msec timer", __func__,
 | 
						|
			   router->rpf_cache_refresh_delay_msec);
 | 
						|
	}
 | 
						|
 | 
						|
	thread_add_timer_msec(router->master, on_rpf_cache_refresh, pim,
 | 
						|
			      router->rpf_cache_refresh_delay_msec,
 | 
						|
			      &pim->rpf_cache_refresher);
 | 
						|
}
 | 
						|
 | 
						|
static void pim_zebra_connected(struct zclient *zclient)
 | 
						|
{
 | 
						|
#if PIM_IPV == 4
 | 
						|
	/* Send the client registration */
 | 
						|
	bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, router->vrf_id);
 | 
						|
#endif
 | 
						|
 | 
						|
	zclient_send_reg_requests(zclient, router->vrf_id);
 | 
						|
 | 
						|
#if PIM_IPV == 4
 | 
						|
	/* request for VxLAN BUM group addresses */
 | 
						|
	pim_zebra_vxlan_replay();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static void pim_zebra_capabilities(struct zclient_capabilities *cap)
 | 
						|
{
 | 
						|
	router->mlag_role = cap->role;
 | 
						|
	router->multipath = cap->ecmp;
 | 
						|
}
 | 
						|
 | 
						|
static zclient_handler *const pim_handlers[] = {
 | 
						|
	[ZEBRA_INTERFACE_ADDRESS_ADD] = pim_zebra_if_address_add,
 | 
						|
	[ZEBRA_INTERFACE_ADDRESS_DELETE] = pim_zebra_if_address_del,
 | 
						|
 | 
						|
	[ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update,
 | 
						|
	[ZEBRA_ROUTER_ID_UPDATE] = pim_router_id_update_zebra,
 | 
						|
	[ZEBRA_INTERFACE_VRF_UPDATE] = pim_zebra_interface_vrf_update,
 | 
						|
 | 
						|
#if PIM_IPV == 4
 | 
						|
	[ZEBRA_VXLAN_SG_ADD] = pim_zebra_vxlan_sg_proc,
 | 
						|
	[ZEBRA_VXLAN_SG_DEL] = pim_zebra_vxlan_sg_proc,
 | 
						|
 | 
						|
	[ZEBRA_MLAG_PROCESS_UP] = pim_zebra_mlag_process_up,
 | 
						|
	[ZEBRA_MLAG_PROCESS_DOWN] = pim_zebra_mlag_process_down,
 | 
						|
	[ZEBRA_MLAG_FORWARD_MSG] = pim_zebra_mlag_handle_msg,
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
void pim_zebra_init(void)
 | 
						|
{
 | 
						|
	/* Socket for receiving updates from Zebra daemon */
 | 
						|
	zclient = zclient_new(router->master, &zclient_options_default,
 | 
						|
			      pim_handlers, array_size(pim_handlers));
 | 
						|
 | 
						|
	zclient->zebra_capabilities = pim_zebra_capabilities;
 | 
						|
	zclient->zebra_connected = pim_zebra_connected;
 | 
						|
 | 
						|
	zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs);
 | 
						|
	if (PIM_DEBUG_PIM_TRACE) {
 | 
						|
		zlog_notice("%s: zclient socket initialized", __func__);
 | 
						|
	}
 | 
						|
 | 
						|
	zclient_lookup_new();
 | 
						|
}
 | 
						|
 | 
						|
void pim_forward_start(struct pim_ifchannel *ch)
 | 
						|
{
 | 
						|
	struct pim_upstream *up = ch->upstream;
 | 
						|
	uint32_t mask = 0;
 | 
						|
 | 
						|
	if (PIM_DEBUG_PIM_TRACE)
 | 
						|
		zlog_debug("%s: (S,G)=%pSG oif=%s (%pPA)", __func__, &ch->sg,
 | 
						|
			   ch->interface->name, &up->upstream_addr);
 | 
						|
 | 
						|
	if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags))
 | 
						|
		mask = PIM_OIF_FLAG_PROTO_GM;
 | 
						|
 | 
						|
	if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags))
 | 
						|
		mask |= PIM_OIF_FLAG_PROTO_PIM;
 | 
						|
 | 
						|
	pim_channel_add_oif(up->channel_oil, ch->interface,
 | 
						|
			mask, __func__);
 | 
						|
}
 | 
						|
 | 
						|
void pim_forward_stop(struct pim_ifchannel *ch)
 | 
						|
{
 | 
						|
	struct pim_upstream *up = ch->upstream;
 | 
						|
 | 
						|
	if (PIM_DEBUG_PIM_TRACE) {
 | 
						|
		zlog_debug("%s: (S,G)=%s oif=%s installed: %d",
 | 
						|
			   __func__, ch->sg_str, ch->interface->name,
 | 
						|
			   up->channel_oil->installed);
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If a channel is being removed, check to see if we still need
 | 
						|
	 * to inherit the interface.  If so make sure it is added in
 | 
						|
	 */
 | 
						|
	if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent))
 | 
						|
		pim_channel_add_oif(up->channel_oil, ch->interface,
 | 
						|
				    PIM_OIF_FLAG_PROTO_PIM, __func__);
 | 
						|
	else
 | 
						|
		pim_channel_del_oif(up->channel_oil, ch->interface,
 | 
						|
				    PIM_OIF_FLAG_PROTO_PIM, __func__);
 | 
						|
}
 | 
						|
 | 
						|
void pim_zebra_zclient_update(struct vty *vty)
 | 
						|
{
 | 
						|
	vty_out(vty, "Zclient update socket: ");
 | 
						|
 | 
						|
	if (zclient) {
 | 
						|
		vty_out(vty, "%d failures=%d\n", zclient->sock, zclient->fail);
 | 
						|
	} else {
 | 
						|
		vty_out(vty, "<null zclient>\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct zclient *pim_zebra_zclient_get(void)
 | 
						|
{
 | 
						|
	if (zclient)
 | 
						|
		return zclient;
 | 
						|
	else
 | 
						|
		return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void pim_zebra_interface_set_master(struct interface *vrf,
 | 
						|
				    struct interface *ifp)
 | 
						|
{
 | 
						|
	zclient_interface_set_master(zclient, vrf, ifp);
 | 
						|
}
 |