// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/fea/mfea_node.cc,v 1.67 2007/02/16 22:45:46 pavlin Exp $"

//
// MFEA (Multicast Forwarding Engine Abstraction) implementation.
//

#include "mfea_module.h"

#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/ipvx.hh"
#include "libxorp/utils.hh"

#include "mrt/max_vifs.h"
#include "mrt/mifset.hh"
#include "mrt/multicast_defs.h"

#include "mfea_mrouter.hh"
#include "mfea_node.hh"
#include "mfea_proto_comm.hh"
#include "mfea_kernel_messages.hh"
#include "mfea_vif.hh"


//
// Exported variables
//

//
// Local constants definitions
//

//
// Local structures/classes, typedefs and macros
//


//
// Local variables
//

//
// Local functions prototypes
//


/**
 * MfeaNode::MfeaNode:
 * @family: The address family (%AF_INET or %AF_INET6
 * for IPv4 and IPv6 respectively).
 * @module_id: The module ID (must be %XORP_MODULE_MFEA).
 * @eventloop: The event loop.
 * 
 * MFEA node constructor.
 **/
MfeaNode::MfeaNode(int family, xorp_module_id module_id,
		   EventLoop& eventloop)
    : ProtoNode<MfeaVif>(family, module_id, eventloop),
      _mfea_mrouter(*this),
      _mfea_dft(*this),
      _is_log_trace(false)
{
    XLOG_ASSERT(module_id == XORP_MODULE_MFEA);
    
    if (module_id != XORP_MODULE_MFEA) {
	XLOG_FATAL("Invalid module ID = %d (must be 'XORP_MODULE_MFEA' = %d)",
		   module_id, XORP_MODULE_MFEA);
    }
    
    for (size_t i = 0; i < _proto_comms.size(); i++)
	_proto_comms[i] = NULL;

    //
    // Set the node status
    //
    ProtoNode<MfeaVif>::set_node_status(PROC_STARTUP);

    //
    // Set myself as an observer when the node status changes
    //
    set_observer(this);
}

/**
 * MfeaNode::~MfeaNode:
 * @: 
 * 
 * MFEA node destructor.
 * 
 **/
MfeaNode::~MfeaNode()
{
    //
    // Unset myself as an observer when the node status changes
    //
    unset_observer(this);

    stop();

    ProtoNode<MfeaVif>::set_node_status(PROC_NULL);
    
    delete_all_vifs();
    
    // Delete the ProtoComm entries
    for (size_t i = 0; i < _proto_comms.size(); i++) {
	if (_proto_comms[i] != NULL)
	    delete _proto_comms[i];
	_proto_comms[i] = NULL;
    }
}

/**
 * MfeaNode::start:
 * @: 
 * 
 * Start the MFEA.
 * TODO: This function should not start the operation on the
 * interfaces. The interfaces must be activated separately.
 * After the startup operations are completed,
 * MfeaNode::final_start() is called to complete the job.
 * 
 * Return value: %XORP_OK on success, otherwize %XORP_ERROR.
 **/
int
MfeaNode::start()
{
    if (! is_enabled())
	return (XORP_OK);

    //
    // Test the service status
    //
    if ((ServiceBase::status() == SERVICE_STARTING)
	|| (ServiceBase::status() == SERVICE_RUNNING)) {
	return (XORP_OK);
    }
    if (ServiceBase::status() != SERVICE_READY) {
	return (XORP_ERROR);
    }

    if (ProtoNode<MfeaVif>::pending_start() < 0)
	return (XORP_ERROR);

    //
    // Set the node status
    //
    ProtoNode<MfeaVif>::set_node_status(PROC_STARTUP);

    //
    // Register with the FEA
    //
    fea_register_startup();

    // Start the MfeaMrouter
    _mfea_mrouter.start();
    
    // Start the ProtoComm entries
    for (size_t i = 0; i < _proto_comms.size(); i++) {
	if (_proto_comms[i] != NULL) {
	    _proto_comms[i]->start();
	}
    }

    return (XORP_OK);
}

/**
 * MfeaNode::final_start:
 * @: 
 * 
 * Completely start the node operation.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::final_start()
{
#if 0	// TODO: XXX: PAVPAVPAV
    if (! is_pending_up())
	return (XORP_ERROR);
#endif

    if (ProtoNode<MfeaVif>::start() < 0) {
	ProtoNode<MfeaVif>::stop();
	return (XORP_ERROR);
    }

    // Start the mfea_vifs
    start_all_vifs();

    XLOG_INFO("MFEA started");

    return (XORP_OK);
}

/**
 * MfeaNode::stop:
 * @: 
 * 
 * Gracefully stop the MFEA.
 * XXX: After the cleanup is completed,
 * MfeaNode::final_stop() is called to complete the job.
 * XXX: This function, unlike start(), will stop the MFEA
 * operation on all interfaces.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::stop()
{
    //
    // Test the service status
    //
    if ((ServiceBase::status() == SERVICE_SHUTDOWN)
	|| (ServiceBase::status() == SERVICE_SHUTTING_DOWN)
	|| (ServiceBase::status() == SERVICE_FAILED)) {
	return (XORP_OK);
    }
    if ((ServiceBase::status() != SERVICE_RUNNING)
	&& (ServiceBase::status() != SERVICE_STARTING)
	&& (ServiceBase::status() != SERVICE_PAUSING)
	&& (ServiceBase::status() != SERVICE_PAUSED)
	&& (ServiceBase::status() != SERVICE_RESUMING)) {
	return (XORP_ERROR);
    }

    if (ProtoNode<MfeaVif>::pending_stop() < 0)
	return (XORP_ERROR);

    //
    // Perform misc. MFEA-specific stop operations
    //
    
    // Stop the vifs
    stop_all_vifs();
    
    // Stop the ProtoComm entries
    for (size_t i = 0; i < _proto_comms.size(); i++) {
	if (_proto_comms[i] != NULL)
	    _proto_comms[i]->stop();
    }

    // Stop the MfeaMrouter
    _mfea_mrouter.stop();

    //
    // Set the node status
    //
    ProtoNode<MfeaVif>::set_node_status(PROC_SHUTDOWN);

    //
    // Update the node status
    //
    update_status();

    return (XORP_OK);
}

/**
 * MfeaNode::final_stop:
 * @: 
 * 
 * Completely stop the MFEA operation.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::final_stop()
{
    if (! (is_up() || is_pending_up() || is_pending_down()))
	return (XORP_ERROR);

    if (ProtoNode<MfeaVif>::stop() < 0)
	return (XORP_ERROR);

    XLOG_INFO("MFEA stopped");

    return (XORP_OK);
}

/**
 * Enable the node operation.
 * 
 * If an unit is not enabled, it cannot be start, or pending-start.
 */
void
MfeaNode::enable()
{
    ProtoUnit::enable();

    XLOG_INFO("MFEA enabled");
}

/**
 * Disable the node operation.
 * 
 * If an unit is disabled, it cannot be start or pending-start.
 * If the unit was runnning, it will be stop first.
 */
void
MfeaNode::disable()
{
    stop();
    ProtoUnit::disable();

    XLOG_INFO("MFEA disabled");
}

void
MfeaNode::status_change(ServiceBase*  service,
			ServiceStatus old_status,
			ServiceStatus new_status)
{
    if (service == this) {
	// My own status has changed
	if ((old_status == SERVICE_STARTING)
	    && (new_status == SERVICE_RUNNING)) {
	    // The startup process has completed
	    if (final_start() < 0) {
		XLOG_ERROR("Cannot complete the startup process; "
			   "current state is %s",
			   ProtoNode<MfeaVif>::state_str().c_str());
		return;
	    }
	    ProtoNode<MfeaVif>::set_node_status(PROC_READY);
	    return;
	}

	if ((old_status == SERVICE_SHUTTING_DOWN)
	    && (new_status == SERVICE_SHUTDOWN)) {
	    // The shutdown process has completed
	    final_stop();
	    // Set the node status
	    ProtoNode<MfeaVif>::set_node_status(PROC_DONE);
	    return;
	}

	//
	// TODO: check if there was an error
	//
	return;
    }

    if (service == ifmgr_mirror_service_base()) {
	if ((old_status == SERVICE_SHUTTING_DOWN)
	    && (new_status == SERVICE_SHUTDOWN)) {
	    MfeaNode::decr_shutdown_requests_n();
	}
    }
}

void
MfeaNode::tree_complete()
{
    //
    // XXX: we use same actions when the tree is completed or updates are made
    //
    updates_made();

    decr_startup_requests_n();
}

void
MfeaNode::updates_made()
{
    map<string, Vif>::iterator mfea_vif_iter;
    string error_msg;

    //
    // Update the local copy of the interface tree
    //
    _iftree = ifmgr_iftree();

    //
    // Add new vifs and update existing ones
    //
    IfMgrIfTree::IfMap::const_iterator ifmgr_iface_iter;
    for (ifmgr_iface_iter = _iftree.ifs().begin();
	 ifmgr_iface_iter != _iftree.ifs().end();
	 ++ifmgr_iface_iter) {
	const IfMgrIfAtom& ifmgr_iface = ifmgr_iface_iter->second;

	IfMgrIfAtom::VifMap::const_iterator ifmgr_vif_iter;
	for (ifmgr_vif_iter = ifmgr_iface.vifs().begin();
	     ifmgr_vif_iter != ifmgr_iface.vifs().end();
	     ++ifmgr_vif_iter) {
	    const IfMgrVifAtom& ifmgr_vif = ifmgr_vif_iter->second;
	    const string& ifmgr_vif_name = ifmgr_vif.name();
	    Vif* node_vif = NULL;
	
	    mfea_vif_iter = configured_vifs().find(ifmgr_vif_name);
	    if (mfea_vif_iter != configured_vifs().end()) {
		node_vif = &(mfea_vif_iter->second);
	    }

	    //
	    // Add a new vif
	    //
	    if (node_vif == NULL) {
		uint32_t vif_index = find_unused_config_vif_index();
		XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID);
		if (add_config_vif(ifmgr_vif_name, vif_index, error_msg) < 0) {
		    XLOG_ERROR("Cannot add vif %s to the set of configured "
			       "vifs: %s",
			       ifmgr_vif_name.c_str(), error_msg.c_str());
		    continue;
		}
		mfea_vif_iter = configured_vifs().find(ifmgr_vif_name);
		XLOG_ASSERT(mfea_vif_iter != configured_vifs().end());
		node_vif = &(mfea_vif_iter->second);
		// FALLTHROUGH
	    }

	    //
	    // Update the pif_index
	    //
	    set_config_pif_index(ifmgr_vif_name,
				 ifmgr_vif.pif_index(),
				 error_msg);
	
	    //
	    // Update the vif flags
	    //
	    bool is_up = ifmgr_iface.enabled();
	    is_up &= (! ifmgr_iface.no_carrier());
	    is_up &= ifmgr_vif.enabled();
	    set_config_vif_flags(ifmgr_vif_name,
				 false,	// is_pim_register
				 ifmgr_vif.p2p_capable(),
				 ifmgr_vif.loopback(),
				 ifmgr_vif.multicast_capable(),
				 ifmgr_vif.broadcast_capable(),
				 is_up,
				 ifmgr_iface.mtu_bytes(),
				 error_msg);
	
	}
    }

    //
    // Add new vif addresses, update existing ones, and remove old addresses
    //
    for (ifmgr_iface_iter = _iftree.ifs().begin();
	 ifmgr_iface_iter != _iftree.ifs().end();
	 ++ifmgr_iface_iter) {
	const IfMgrIfAtom& ifmgr_iface = ifmgr_iface_iter->second;
	const string& ifmgr_iface_name = ifmgr_iface.name();
	IfMgrIfAtom::VifMap::const_iterator ifmgr_vif_iter;

	for (ifmgr_vif_iter = ifmgr_iface.vifs().begin();
	     ifmgr_vif_iter != ifmgr_iface.vifs().end();
	     ++ifmgr_vif_iter) {
	    const IfMgrVifAtom& ifmgr_vif = ifmgr_vif_iter->second;
	    const string& ifmgr_vif_name = ifmgr_vif.name();
	    Vif* node_vif = NULL;

	    //
	    // Add new vif addresses and update existing ones
	    //
	    mfea_vif_iter = configured_vifs().find(ifmgr_vif_name);
	    if (mfea_vif_iter != configured_vifs().end()) {
		node_vif = &(mfea_vif_iter->second);
	    }

	    if (is_ipv4()) {
		IfMgrVifAtom::V4Map::const_iterator a4_iter;

		for (a4_iter = ifmgr_vif.ipv4addrs().begin();
		     a4_iter != ifmgr_vif.ipv4addrs().end();
		     ++a4_iter) {
		    const IfMgrIPv4Atom& a4 = a4_iter->second;
		    VifAddr* node_vif_addr = node_vif->find_address(IPvX(a4.addr()));
		    IPvX addr(a4.addr());
		    IPvXNet subnet_addr(addr, a4.prefix_len());
		    IPvX broadcast_addr(IPvX::ZERO(family()));
		    IPvX peer_addr(IPvX::ZERO(family()));
		    if (a4.has_broadcast())
			broadcast_addr = IPvX(a4.broadcast_addr());
		    if (a4.has_endpoint())
			peer_addr = IPvX(a4.endpoint_addr());

		    if (node_vif_addr == NULL) {
			if (add_config_vif_addr(
				ifmgr_vif_name,
				addr,
				subnet_addr,
				broadcast_addr,
				peer_addr,
				error_msg) < 0) {
			    XLOG_ERROR("Cannot add address %s to vif %s from "
				       "the set of configured vifs: %s",
				       cstring(addr), ifmgr_vif_name.c_str(),
				       error_msg.c_str());
			}
			continue;
		    }
		    if ((addr == node_vif_addr->addr())
			&& (subnet_addr == node_vif_addr->subnet_addr())
			&& (broadcast_addr == node_vif_addr->broadcast_addr())
			&& (peer_addr == node_vif_addr->peer_addr())) {
			continue;	// Nothing changed
		    }

		    // Update the address
		    if (delete_config_vif_addr(ifmgr_vif_name,
					       addr,
					       error_msg) < 0) {
			XLOG_ERROR("Cannot delete address %s from vif %s "
				   "from the set of configured vifs: %s",
				   cstring(addr),
				   ifmgr_vif_name.c_str(),
				   error_msg.c_str());
		    }
		    if (add_config_vif_addr(
			    ifmgr_vif_name,
			    addr,
			    subnet_addr,
			    broadcast_addr,
			    peer_addr,
			    error_msg) < 0) {
			XLOG_ERROR("Cannot add address %s to vif %s from "
				   "the set of configured vifs: %s",
				   cstring(addr), ifmgr_vif_name.c_str(),
				   error_msg.c_str());
		    }
		}
	    }

	    if (is_ipv6()) {
		IfMgrVifAtom::V6Map::const_iterator a6_iter;

		for (a6_iter = ifmgr_vif.ipv6addrs().begin();
		     a6_iter != ifmgr_vif.ipv6addrs().end();
		     ++a6_iter) {
		    const IfMgrIPv6Atom& a6 = a6_iter->second;
		    VifAddr* node_vif_addr = node_vif->find_address(IPvX(a6.addr()));
		    IPvX addr(a6.addr());
		    IPvXNet subnet_addr(addr, a6.prefix_len());
		    IPvX broadcast_addr(IPvX::ZERO(family()));
		    IPvX peer_addr(IPvX::ZERO(family()));
		    if (a6.has_endpoint())
			peer_addr = IPvX(a6.endpoint_addr());

		    if (node_vif_addr == NULL) {
			if (add_config_vif_addr(
				ifmgr_vif_name,
				addr,
				subnet_addr,
				broadcast_addr,
				peer_addr,
				error_msg) < 0) {
			    XLOG_ERROR("Cannot add address %s to vif %s from "
				       "the set of configured vifs: %s",
				       cstring(addr), ifmgr_vif_name.c_str(),
				       error_msg.c_str());
			}
			continue;
		    }
		    if ((addr == node_vif_addr->addr())
			&& (subnet_addr == node_vif_addr->subnet_addr())
			&& (peer_addr == node_vif_addr->peer_addr())) {
			continue;	// Nothing changed
		    }

		    // Update the address
		    if (delete_config_vif_addr(ifmgr_vif_name,
					       addr,
					       error_msg) < 0) {
			XLOG_ERROR("Cannot delete address %s from vif %s "
				   "from the set of configured vifs: %s",
				   cstring(addr),
				   ifmgr_vif_name.c_str(),
				   error_msg.c_str());
		    }
		    if (add_config_vif_addr(
			    ifmgr_vif_name,
			    addr,
			    subnet_addr,
			    broadcast_addr,
			    peer_addr,
			    error_msg) < 0) {
			XLOG_ERROR("Cannot add address %s to vif %s from "
				   "the set of configured vifs: %s",
				   cstring(addr), ifmgr_vif_name.c_str(),
				   error_msg.c_str());
		    }
		}
	    }

	    //
	    // Delete vif addresses that don't exist anymore
	    //
	    {
		list<IPvX> delete_addresses_list;
		list<VifAddr>::const_iterator vif_addr_iter;
		for (vif_addr_iter = node_vif->addr_list().begin();
		     vif_addr_iter != node_vif->addr_list().end();
		     ++vif_addr_iter) {
		    const VifAddr& vif_addr = *vif_addr_iter;
		    if (vif_addr.addr().is_ipv4()
			&& (_iftree.find_addr(ifmgr_iface_name,
					      ifmgr_vif_name,
					      vif_addr.addr().get_ipv4()))
			    == NULL) {
			    delete_addresses_list.push_back(vif_addr.addr());
		    }
		    if (vif_addr.addr().is_ipv6()
			&& (_iftree.find_addr(ifmgr_iface_name,
					      ifmgr_vif_name,
					      vif_addr.addr().get_ipv6()))
			    == NULL) {
			    delete_addresses_list.push_back(vif_addr.addr());
		    }
		}

		// Delete the addresses
		list<IPvX>::iterator ipvx_iter;
		for (ipvx_iter = delete_addresses_list.begin();
		     ipvx_iter != delete_addresses_list.end();
		     ++ipvx_iter) {
		    const IPvX& ipvx = *ipvx_iter;
		    if (delete_config_vif_addr(ifmgr_vif_name, ipvx, error_msg)
			< 0) {
			XLOG_ERROR("Cannot delete address %s from vif %s from "
				   "the set of configured vifs: %s",
				   cstring(ipvx), ifmgr_vif_name.c_str(),
				   error_msg.c_str());
		    }
		}
	    }
	}
    }

    //
    // Remove vifs that don't exist anymore
    //
    list<string> delete_vifs_list;
    for (mfea_vif_iter = configured_vifs().begin();
	 mfea_vif_iter != configured_vifs().end();
	 ++mfea_vif_iter) {
	Vif* node_vif = &mfea_vif_iter->second;
	if (node_vif->is_pim_register())
	    continue;		// XXX: don't delete the PIM Register vif
	if (_iftree.find_vif(node_vif->name(), node_vif->name()) == NULL) {
	    // Add the vif to the list of old interfaces
	    delete_vifs_list.push_back(node_vif->name());
	}
    }
    // Delete the old vifs
    list<string>::iterator vif_name_iter;
    for (vif_name_iter = delete_vifs_list.begin();
	 vif_name_iter != delete_vifs_list.end();
	 ++vif_name_iter) {
	const string& vif_name = *vif_name_iter;
	if (delete_config_vif(vif_name, error_msg) < 0) {
	    XLOG_ERROR("Cannot delete vif %s from the set of configured "
		       "vifs: %s",
		       vif_name.c_str(), error_msg.c_str());
	}
    }
    
    // Done
    set_config_all_vifs_done(error_msg);
}

/**
 * MfeaNode::add_vif:
 * @vif: Vif information about the new MfeaVif to install.
 * @error_msg: The error message (if error).
 * 
 * Install a new MFEA vif.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::add_vif(const Vif& vif, string& error_msg)
{
    //
    // Create a new MfeaVif
    //
    MfeaVif *mfea_vif = new MfeaVif(*this, vif);
    
    if (ProtoNode<MfeaVif>::add_vif(mfea_vif) != XORP_OK) {
	// Cannot add this new vif
	error_msg = c_format("Cannot add vif %s: internal error",
			     vif.name().c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	
	delete mfea_vif;
	return (XORP_ERROR);
    }
    
    XLOG_INFO("Interface added: %s", mfea_vif->str().c_str());
    
    return (XORP_OK);
}

/**
 * MfeaNode::add_pim_register_vif:
 * 
 * Install a new MFEA PIM Register vif (if needed).
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::add_pim_register_vif()
{
    string error_msg;

    //
    // Test first whether we have already PIM Register vif
    //
    for (uint32_t i = 0; i < maxvifs(); i++) {
	MfeaVif *mfea_vif = vif_find_by_vif_index(i);
	if (mfea_vif == NULL)
	    continue;
	if (mfea_vif->is_pim_register())
	    return (XORP_OK);		// Found: OK
    }
    
    //
    // Create the PIM Register vif if there is a valid IP address
    // on an interface that is already up and running.
    //
    // TODO: check with Linux, Solaris, etc, if we can
    // use 127.0.0.2 or ::2 as a PIM Register vif address, and use that
    // address instead (otherwise we may always have to keep track
    // whether the underlying address has changed).
    //
    bool mfea_vif_found = false;
    MfeaVif *mfea_vif = NULL;
    for (uint32_t i = 0; i < maxvifs(); i++) {
	mfea_vif = vif_find_by_vif_index(i);
	if (mfea_vif == NULL)
	    continue;
	if (! mfea_vif->is_underlying_vif_up())
	    continue;
	if (! mfea_vif->is_up())
	    continue;
	if (mfea_vif->addr_ptr() == NULL)
	    continue;
	if (mfea_vif->is_pim_register())
	    continue;
	if (mfea_vif->is_loopback())
	    continue;
	if (! mfea_vif->is_multicast_capable())
	    continue;
	// Found appropriate vif
	mfea_vif_found = true;
	break;
    }
    if (mfea_vif_found) {
	// Add the Register vif
	uint32_t vif_index = find_unused_config_vif_index();
	XLOG_ASSERT(vif_index != Vif::VIF_INDEX_INVALID);
	// TODO: XXX: the Register vif name is hardcoded here!
	MfeaVif register_vif(*this, Vif("register_vif"));
	register_vif.set_vif_index(vif_index);
	register_vif.set_pif_index(mfea_vif->pif_index());
	register_vif.set_underlying_vif_up(true); // XXX: 'true' to allow creation
	register_vif.set_pim_register(true);
	register_vif.set_mtu(mfea_vif->mtu());
	// Add all addresses, but ignore subnets, broadcast and p2p addresses
	list<VifAddr>::const_iterator vif_addr_iter;
	for (vif_addr_iter = mfea_vif->addr_list().begin();
	     vif_addr_iter != mfea_vif->addr_list().end();
	     ++vif_addr_iter) {
	    const VifAddr& vif_addr = *vif_addr_iter;
	    const IPvX& ipvx = vif_addr.addr();
	    register_vif.add_address(ipvx, IPvXNet(ipvx, ipvx.addr_bitlen()),
				     ipvx, IPvX::ZERO(family()));
	}
	if (add_vif(register_vif, error_msg) < 0) {
	    XLOG_ERROR("Cannot add Register vif: %s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	
	if (add_config_vif(register_vif, error_msg) < 0) {
	    XLOG_ERROR("Cannot add Register vif to set of configured vifs: %s",
		       error_msg.c_str());
	    return (XORP_ERROR);
	}
    }

    // Done
    set_config_all_vifs_done(error_msg);

    return (XORP_OK);
}

/**
 * MfeaNode::delete_vif:
 * @vif_name: The name of the vif to delete.
 * @error_msg: The error message (if error).
 * 
 * Delete an existing MFEA vif.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::delete_vif(const string& vif_name, string& error_msg)
{
    MfeaVif *mfea_vif = vif_find_by_name(vif_name);
    if (mfea_vif == NULL) {
	error_msg = c_format("Cannot delete vif %s: no such vif",
		       vif_name.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    if (ProtoNode<MfeaVif>::delete_vif(mfea_vif) != XORP_OK) {
	error_msg = c_format("Cannot delete vif %s: internal error",
			     vif_name.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	delete mfea_vif;
	return (XORP_ERROR);
    }
    
    delete mfea_vif;
    
    XLOG_INFO("Interface deleted: %s", vif_name.c_str());
    
    return (XORP_OK);
}

/**
 * MfeaNode::enable_vif:
 * @vif_name: The name of the vif to enable.
 * @error_msg: The error message (if error).
 * 
 * Enable an existing MFEA vif.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::enable_vif(const string& vif_name, string& error_msg)
{
    MfeaVif *mfea_vif = vif_find_by_name(vif_name);
    if (mfea_vif == NULL) {
	error_msg = c_format("Cannot enable vif %s: no such vif",
			     vif_name.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    mfea_vif->enable();
    
    return (XORP_OK);
}

/**
 * MfeaNode::disable_vif:
 * @vif_name: The name of the vif to disable.
 * @error_msg: The error message (if error).
 * 
 * Disable an existing MFEA vif.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::disable_vif(const string& vif_name, string& error_msg)
{
    MfeaVif *mfea_vif = vif_find_by_name(vif_name);
    if (mfea_vif == NULL) {
	error_msg = c_format("Cannot disable vif %s: no such vif",
			     vif_name.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    mfea_vif->disable();
    
    return (XORP_OK);
}

/**
 * MfeaNode::start_vif:
 * @vif_name: The name of the vif to start.
 * @error_msg: The error message (if error).
 * 
 * Start an existing MFEA vif.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::start_vif(const string& vif_name, string& error_msg)
{
    MfeaVif *mfea_vif = vif_find_by_name(vif_name);
    if (mfea_vif == NULL) {
	error_msg = c_format("Cannot start vif %s: no such vif",
			     vif_name.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    if (mfea_vif->start(error_msg) != XORP_OK) {
	error_msg = c_format("Cannot start vif %s: %s",
			     vif_name.c_str(), error_msg.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    // XXX: add PIM Register vif (if needed)
    add_pim_register_vif();

    return (XORP_OK);
}

/**
 * MfeaNode::stop_vif:
 * @vif_name: The name of the vif to stop.
 * @error_msg: The error message (if error).
 * 
 * Stop an existing MFEA vif.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::stop_vif(const string& vif_name, string& error_msg)
{
    MfeaVif *mfea_vif = vif_find_by_name(vif_name);
    if (mfea_vif == NULL) {
	error_msg = c_format("Cannot stop vif %s: no such vif",
			     vif_name.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    if (mfea_vif->stop(error_msg) != XORP_OK) {
	error_msg = c_format("Cannot stop vif %s: %s",
			     vif_name.c_str(), error_msg.c_str());
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::start_all_vifs:
 * @: 
 * 
 * Start MFEA on all enabled interfaces.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::start_all_vifs()
{
    vector<MfeaVif *>::iterator iter;
    string error_msg;
    int ret_value = XORP_OK;
    
    for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) {
	MfeaVif *mfea_vif = (*iter);
	if (mfea_vif == NULL)
	    continue;
	if (start_vif(mfea_vif->name(), error_msg) != XORP_OK)
	    ret_value = XORP_ERROR;
    }
    
    return (ret_value);
}

/**
 * MfeaNode::stop_all_vifs:
 * @: 
 * 
 * Stop MFEA on all interfaces it was running on.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::stop_all_vifs()
{
    vector<MfeaVif *>::iterator iter;
    string error_msg;
    int ret_value = XORP_OK;
    
    for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) {
	MfeaVif *mfea_vif = (*iter);
	if (mfea_vif == NULL)
	    continue;
	if (stop_vif(mfea_vif->name(), error_msg) != XORP_OK)
	    ret_value = XORP_ERROR;
    }
    
    return (ret_value);
}

/**
 * MfeaNode::enable_all_vifs:
 * @: 
 * 
 * Enable MFEA on all interfaces.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::enable_all_vifs()
{
    vector<MfeaVif *>::iterator iter;
    string error_msg;
    int ret_value = XORP_OK;
    
    for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) {
	MfeaVif *mfea_vif = (*iter);
	if (mfea_vif == NULL)
	    continue;
	if (enable_vif(mfea_vif->name(), error_msg) != XORP_OK)
	    ret_value = XORP_ERROR;
    }
    
    return (ret_value);
}

/**
 * MfeaNode::disable_all_vifs:
 * @: 
 * 
 * Disable MFEA on all interfaces. All running interfaces are stopped first.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::disable_all_vifs()
{
    vector<MfeaVif *>::iterator iter;
    string error_msg;
    int ret_value = XORP_OK;
    
    for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) {
	MfeaVif *mfea_vif = (*iter);
	if (mfea_vif == NULL)
	    continue;
	if (disable_vif(mfea_vif->name(), error_msg) != XORP_OK)
	    ret_value = XORP_ERROR;
    }
    
    return (ret_value);
}

/**
 * MfeaNode::delete_all_vifs:
 * @: 
 * 
 * Delete all MFEA vifs.
 **/
void
MfeaNode::delete_all_vifs()
{
    list<string> vif_names;
    vector<MfeaVif *>::iterator iter;

    //
    // Create the list of all vif names to delete
    //
    for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) {
	MfeaVif *mfea_vif = (*iter);
	if (mfea_vif != NULL) {
	    string vif_name = mfea_vif->name();
	    vif_names.push_back(mfea_vif->name());
	}
    }

    //
    // Delete all vifs
    //
    list<string>::iterator vif_names_iter;
    for (vif_names_iter = vif_names.begin();
	 vif_names_iter != vif_names.end();
	 ++vif_names_iter) {
	const string& vif_name = *vif_names_iter;
	string error_msg;
	if (delete_vif(vif_name, error_msg) != XORP_OK) {
	    error_msg = c_format("Cannot delete vif %s: internal error",
				 vif_name.c_str());
	    XLOG_ERROR("%s", error_msg.c_str());
	}
    }
}

/**
 * A method called when a vif has completed its shutdown.
 * 
 * @param vif_name the name of the vif that has completed its shutdown.
 */
void
MfeaNode::vif_shutdown_completed(const string& vif_name)
{
    vector<MfeaVif *>::iterator iter;

    UNUSED(vif_name);

    //
    // If all vifs have completed the shutdown, then de-register with
    // the MFEA.
    //
    for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) {
	MfeaVif *mfea_vif = *iter;
	if (mfea_vif == NULL)
	    continue;
	if (! mfea_vif->is_down())
	    return;
    }

    //
    // De-register with the FEA
    //
    fea_register_shutdown();
}

/**
 * MfeaNode::add_protocol:
 * @module_instance_name: The module instance name of the protocol to add.
 * @module_id: The #xorp_module_id of the protocol to add.
 * 
 * A method used by a protocol instance to register with this #MfeaNode.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::add_protocol(const string& module_instance_name,
		       xorp_module_id module_id)
{
    ProtoComm *proto_comm;
    int ip_protocol;
    size_t i;
    
    // Add the state
    if (_proto_register.add_protocol(module_instance_name, module_id) < 0) {
	XLOG_ERROR("Cannot add protocol instance %s with module_id = %d",
		   module_instance_name.c_str(), module_id);
	return (XORP_ERROR);	// Already added
    }
    
    // Test if we have already the appropriate ProtoComm
    if (proto_comm_find_by_module_id(module_id) != NULL)
	return (XORP_OK);
    
    //
    // Get the IP protocol number (IPPROTO_*)
    //
    ip_protocol = -1;
    switch (module_id) {
    case XORP_MODULE_MLD6IGMP:
	switch (family()) {
	case AF_INET:
	    ip_protocol = IPPROTO_IGMP;
	    break;
#ifdef HAVE_IPV6
	case AF_INET6:
	    ip_protocol = IPPROTO_ICMPV6;
	    break;
#endif // HAVE_IPV6
	default:
	    XLOG_UNREACHABLE();
	    _proto_register.delete_protocol(module_instance_name, module_id);
	    return (XORP_ERROR);
	}
	break;
    case XORP_MODULE_PIMSM:
    case XORP_MODULE_PIMDM:
	ip_protocol = IPPROTO_PIM;
	break;
    default:
	XLOG_UNREACHABLE();
	_proto_register.delete_protocol(module_instance_name, module_id);
	return (XORP_ERROR);
    }
    
    proto_comm = new ProtoComm(*this, ip_protocol, module_id);
    
    // Add the new entry
    for (i = 0; i < _proto_comms.size(); i++) {
	if (_proto_comms[i] == NULL)
	    break;
    }
    if (i < _proto_comms.size()) {
	_proto_comms[i] = proto_comm;
    } else {
	_proto_comms.push_back(proto_comm);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::delete_protocol:
 * @module_instance_name: The module instance name of the protocol to delete.
 * @module_id: The #xorp_module_id of the protocol to delete.
 * 
 * A method used by a protocol instance to deregister with this #MfeaNode.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::delete_protocol(const string& module_instance_name,
			  xorp_module_id module_id)
{
    vector<MfeaVif *>::iterator iter;

    // Explicitly stop the protocol on all vifs
    for (iter = proto_vifs().begin(); iter != proto_vifs().end(); ++iter) {
	MfeaVif *mfea_vif = (*iter);
	if (mfea_vif == NULL)
	    continue;
	// TODO: XXX: PAVPAVPAV: shall we ignore the disabled vifs??
	mfea_vif->stop_protocol(module_instance_name, module_id);
    }
    
    // Delete kernel signal registration
    if (_kernel_signal_messages_register.is_registered(module_instance_name,
						       module_id)) {
	delete_allow_kernel_signal_messages(module_instance_name, module_id);
    }
    
    // Delete the state
    if (_proto_register.delete_protocol(module_instance_name, module_id) < 0) {
	XLOG_ERROR("Cannot delete protocol instance %s with module_id = %d",
		   module_instance_name.c_str(), module_id);
	return (XORP_ERROR);	// Probably not added before
    }
    
    if (! _proto_register.is_registered(module_id)) {
	//
	// The last registered protocol instance
	//
	ProtoComm *proto_comm = proto_comm_find_by_module_id(module_id);
	
	if (proto_comm == NULL)
	    return (XORP_ERROR);
	
	// Remove the pointer storage for this ProtoComm entry
	for (size_t i = 0; i < _proto_comms.size(); i++) {
	    if (_proto_comms[i] == proto_comm) {
		_proto_comms[i] = NULL;
		break;
	    }
	}
	if (_proto_comms[_proto_comms.size() - 1] == NULL) {
	    // Remove the last entry, if not used anymore
	    _proto_comms.pop_back();
	}
	
	delete proto_comm;
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::start_protocol:
 * @module_id: The #xorp_module_id of the protocol to start.
 * 
 * Start operation for protocol with #xorp_module_id of @module_id.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::start_protocol(xorp_module_id module_id)
{
    ProtoComm *proto_comm = proto_comm_find_by_module_id(module_id);
    
    if (proto_comm == NULL)
	return (XORP_ERROR);
    
    if (proto_comm->is_up())
	return (XORP_OK);		// Already running
    
    if (proto_comm->start() < 0)
	return (XORP_ERROR);
    
    return (XORP_OK);
}

/**
 * MfeaNode::stop_protocol:
 * @module_id: The #xorp_module_id of the protocol to stop.
 * 
 * Stop operation for protocol with #xorp_module_id of @module_id.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::stop_protocol(xorp_module_id module_id)
{
    ProtoComm *proto_comm = proto_comm_find_by_module_id(module_id);
    
    if (proto_comm == NULL)
	return (XORP_ERROR);
    
    if (proto_comm->stop() < 0)
	return (XORP_ERROR);
    
    return (XORP_OK);
}

/**
 * MfeaNode::start_protocol_vif:
 * @module_instance_name: The module instance name of the protocol to start
 * on vif with vif_index of @vif_index.
 * @module_id: The #xorp_module_id of the protocol to start on vif with
 * vif_index of @vif_index.
 * @vif_index: The index of the vif the protocol start to apply to.
 * 
 * Start a protocol on an interface with vif_index of @vif_index.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::start_protocol_vif(const string& module_instance_name,
			     xorp_module_id module_id,
			     uint32_t vif_index)
{
    MfeaVif *mfea_vif = vif_find_by_vif_index(vif_index);
    
    if (mfea_vif == NULL) {
	XLOG_ERROR("Cannot start protocol instance %s on vif_index %d: "
		   "no such vif",
		   module_instance_name.c_str(), vif_index);
	return (XORP_ERROR);
    }
    
    if (mfea_vif->start_protocol(module_instance_name, module_id) < 0)
	return (XORP_ERROR);
    
    return (XORP_OK);
}

/**
 * MfeaNode::stop_protocol_vif:
 * @module_instance_name: The module instance name of the protocol to stop
 * on vif with vif_index of @vif_index.
 * @module_id: The #xorp_module_id of the protocol to stop on vif with
 * vif_index of @vif_index.
 * @vif_index: The index of the vif the protocol stop to apply to.
 * 
 * Stop a protocol on an interface with vif_index of @vif_index.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::stop_protocol_vif(const string& module_instance_name,
			    xorp_module_id module_id,
			    uint32_t vif_index)
{
    MfeaVif *mfea_vif = vif_find_by_vif_index(vif_index);
    
    if (mfea_vif == NULL) {
	XLOG_ERROR("Cannot stop protocol instance %s on vif_index %d: "
		   "no such vif",
		   module_instance_name.c_str(), vif_index);
	return (XORP_ERROR);
    }
    
    if (mfea_vif->stop_protocol(module_instance_name, module_id) < 0)
	return (XORP_ERROR);
    
    return (XORP_OK);
}

/**
 * MfeaNode::add_allow_kernel_signal_messages:
 * @module_instance_name: The module instance name of the protocol to add.
 * @module_id: The #xorp_module_id of the protocol to add to receive kernel
 * signal messages.
 * 
 * Add a protocol to the set of protocols that are interested in
 * receiving kernel signal messages.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::add_allow_kernel_signal_messages(const string& module_instance_name,
					   xorp_module_id module_id)
{
    // Add the state
    if (_kernel_signal_messages_register.add_protocol(module_instance_name,
						      module_id)
	< 0) {
	XLOG_ERROR("Cannot add protocol instance %s with module_id = %d "
		   "to receive kernel signal messages",
		   module_instance_name.c_str(), module_id);
	return (XORP_ERROR);	// Already added
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::delete_allow_kernel_signal_messages:
 * @module_instance_name: The module instance name of the protocol to delete.
 * @module_id: The #xorp_module_id of the protocol to delete from receiving
 * kernel signal messages.
 * 
 * Delete a protocol from the set of protocols that are interested in
 * receiving kernel signal messages.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::delete_allow_kernel_signal_messages(const string& module_instance_name,
					      xorp_module_id module_id)
{
    // Delete the state
    if (_kernel_signal_messages_register.delete_protocol(module_instance_name,
							 module_id)
	< 0) {
	XLOG_ERROR("Cannot delete protocol instance %s with module_id = %d "
		   "from receiving kernel signal messages",
		   module_instance_name.c_str(), module_id);
	return (XORP_ERROR);	// Probably not added before
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::proto_recv:
 * @src_module_instance_name: The module instance name of the module-origin
 * of the message.
 * @src_module_id: The #xorp_module_id of the module-origin of the message.
 * @vif_index: The vif index of the interface to use to send this message.
 * @src: The source address of the message.
 * @dst: The destination address of the message.
 * @ip_ttl: The IP TTL of the message. If it has a negative value,
 * it should be ignored.
 * @ip_tos: The IP TOS of the message. If it has a negative value,
 * it should be ignored.
 * @is_router_alert: If true, set the Router Alert IP option for the IP
 * packet of the outgoung message.
 * @rcvbuf: The data buffer with the message to send.
 * @rcvlen: The data length in @rcvbuf.
 * @error_msg: The error message (if error).
 * 
 * Receive a protocol message from a user-level process, and send it
 * through the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::proto_recv(const string&	, // src_module_instance_name,
		     xorp_module_id src_module_id,
		     uint32_t vif_index,
		     const IPvX& src, const IPvX& dst,
		     int ip_ttl, int ip_tos, bool is_router_alert,
		     const uint8_t *rcvbuf, size_t rcvlen, string& error_msg)
{
    ProtoComm *proto_comm;
    
    if (! is_up()) {
	error_msg = c_format("MFEA node is not UP");
	return (XORP_ERROR);
    }
    
    // TODO: for now @src_module_id that comes by the
    // upper-layer protocol is used to find-out the ProtoComm entry.
    proto_comm = proto_comm_find_by_module_id(src_module_id);
    if (proto_comm == NULL) {
	error_msg = c_format("Protocol with module ID %u is not registered",
			     XORP_UINT_CAST(src_module_id));
	return (XORP_ERROR);
    }
    
    if (proto_comm->proto_socket_write(vif_index,
				       src, dst,
				       ip_ttl,
				       ip_tos,
				       is_router_alert,
				       rcvbuf,
				       rcvlen,
				       error_msg) < 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

// The function to process incoming messages from the kernel
int
MfeaNode::proto_comm_recv(xorp_module_id dst_module_id,
			  uint32_t vif_index,
			  const IPvX& src, const IPvX& dst,
			  int ip_ttl, int ip_tos, bool is_router_alert,
			  const uint8_t *rcvbuf, size_t rcvlen)
{
    string error_msg;

    XLOG_TRACE(false & is_log_trace(),	// XXX: unconditionally disabled
	       "RX packet for dst_module_name %s: "
	       "vif_index = %d src = %s dst = %s ttl = %d tos = %#x "
	       "router_alert = %d rcvbuf = %p rcvlen = %u",
	       xorp_module_name(family(), dst_module_id), vif_index,
	       cstring(src), cstring(dst), ip_ttl, ip_tos, is_router_alert,
	       rcvbuf, XORP_UINT_CAST(rcvlen));
    
    //
    // Test if we should accept or drop the message
    //
    MfeaVif *mfea_vif = vif_find_by_vif_index(vif_index);
    if (mfea_vif == NULL)
	return (XORP_ERROR);
    ProtoRegister& pr = mfea_vif->proto_register();
    if (! pr.is_registered(dst_module_id))
	return (XORP_ERROR);	// The message is not expected
    
    if (! is_up())
	return (XORP_ERROR);
    
    //
    // Send the message to all interested protocol instances
    //

    list<string>::const_iterator iter;
    for (iter = pr.module_instance_name_list(dst_module_id).begin();
	 iter != pr.module_instance_name_list(dst_module_id).end();
	 ++iter) {
	const string& dst_module_instance_name = *iter;
	proto_send(dst_module_instance_name,
		   dst_module_id,
		   vif_index,
		   src, dst,
		   ip_ttl,
		   ip_tos,
		   is_router_alert,
		   rcvbuf,
		   rcvlen,
		   error_msg);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::signal_message_recv:
 * @src_module_instance_name: Unused.
 * @src_module_id: The #xorp_module_id module ID of the associated #ProtoComm
 * entry. XXX: in the future it may become irrelevant.
 * @message_type: The message type of the kernel signal
 * (%IGMPMSG_* or %MRT6MSG_*)
 * @vif_index: The vif index of the related interface (message-specific).
 * @src: The source address in the message.
 * @dst: The destination address in the message.
 * @rcvbuf: The data buffer with the additional information in the message.
 * @rcvlen: The data length in @rcvbuf.
 * 
 * Process NOCACHE, WRONGVIF/WRONGMIF, WHOLEPKT, BW_UPCALL signals from the
 * kernel. The signal is sent to all user-level protocols that expect it.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::signal_message_recv(const string&	, // src_module_instance_name,
			      xorp_module_id src_module_id,
			      int message_type,
			      uint32_t vif_index,
			      const IPvX& src, const IPvX& dst,
			      const uint8_t *rcvbuf, size_t rcvlen)
{
    XLOG_TRACE(is_log_trace(),
	       "RX kernel signal: "
	       "message_type = %d vif_index = %d src = %s dst = %s",
	       message_type, vif_index,
	       cstring(src), cstring(dst));

    UNUSED(src_module_id);
    
    if (! is_up())
	return (XORP_ERROR);
    
    //
    // If it is a bandwidth upcall message, parse it now
    //
    if (message_type == MFEA_KERNEL_MESSAGE_BW_UPCALL) {
	//
	// XXX: do we need to check if the kernel supports the
	// bandwidth-upcall mechanism?
	//
	
	//
	// Do the job
	//
	switch (family()) {
	case AF_INET:
	{
#if defined(MRT_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	    size_t from = 0;
	    struct bw_upcall bw_upcall;
	    IPvX src(family()), dst(family());
	    bool is_threshold_in_packets, is_threshold_in_bytes;
	    bool is_geq_upcall, is_leq_upcall;
	    
	    while (rcvlen >= sizeof(bw_upcall)) {
		memcpy(&bw_upcall, rcvbuf + from, sizeof(bw_upcall));
		rcvlen -= sizeof(bw_upcall);
		from += sizeof(bw_upcall);
		
		src.copy_in(bw_upcall.bu_src);
		dst.copy_in(bw_upcall.bu_dst);
		is_threshold_in_packets
		    = bw_upcall.bu_flags & BW_UPCALL_UNIT_PACKETS;
		is_threshold_in_bytes
		    = bw_upcall.bu_flags & BW_UPCALL_UNIT_BYTES;
		is_geq_upcall = bw_upcall.bu_flags & BW_UPCALL_GEQ;
		is_leq_upcall = bw_upcall.bu_flags & BW_UPCALL_LEQ;
		signal_dataflow_message_recv(
		    src,
		    dst,
		    TimeVal(bw_upcall.bu_threshold.b_time),
		    TimeVal(bw_upcall.bu_measured.b_time),
		    bw_upcall.bu_threshold.b_packets,
		    bw_upcall.bu_threshold.b_bytes,
		    bw_upcall.bu_measured.b_packets,
		    bw_upcall.bu_measured.b_bytes,
		    is_threshold_in_packets,
		    is_threshold_in_bytes,
		    is_geq_upcall,
		    is_leq_upcall);
	    }
#endif // MRT_ADD_BW_UPCALL && ENABLE_ADVANCED_MULTICAST_API
	}
	break;
	
#ifdef HAVE_IPV6
	case AF_INET6:
	{
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("signal_message_recv() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	
#if defined(MRT6_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	    size_t from = 0;
	    struct bw6_upcall bw_upcall;
	    IPvX src(family()), dst(family());
	    bool is_threshold_in_packets, is_threshold_in_bytes;
	    bool is_geq_upcall, is_leq_upcall;
	    
	    while (rcvlen >= sizeof(bw_upcall)) {
		memcpy(&bw_upcall, rcvbuf + from, sizeof(bw_upcall));
		rcvlen -= sizeof(bw_upcall);
		from += sizeof(bw_upcall);
		
		src.copy_in(bw_upcall.bu6_src);
		dst.copy_in(bw_upcall.bu6_dsr);
		is_threshold_in_packets
		    = bw_upcall.bu6_flags & BW_UPCALL_UNIT_PACKETS;
		is_threshold_in_bytes
		    = bw_upcall.bu6_flags & BW_UPCALL_UNIT_BYTES;
		is_geq_upcall = bw_upcall.bu6_flags & BW_UPCALL_GEQ;
		is_leq_upcall = bw_upcall.bu6_flags & BW_UPCALL_LEQ;
		signal_dataflow_message_recv(
		    src,
		    dst,
		    TimeVal(bw_upcall.bu6_threshold.b_time),
		    TimeVal(bw_upcall.bu6_measured.b_time),
		    bw_upcall.bu6_threshold.b_packets,
		    bw_upcall.bu6_threshold.b_bytes,
		    bw_upcall.bu6_measured.b_packets,
		    bw_upcall.bu6_measured.b_bytes,
		    is_threshold_in_packets,
		    is_threshold_in_bytes,
		    is_geq_upcall,
		    is_leq_upcall);
	    }
#endif // MRT6_ADD_BW_UPCALL && ENABLE_ADVANCED_MULTICAST_API
#endif // HAVE_IPV6_MULTICAST_ROUTING
	}
	break;
#endif // HAVE_IPV6
	
	default:
	    XLOG_UNREACHABLE();
	    return (XORP_ERROR);
	}
	return (XORP_OK);
    }
    
    //
    // Test if we should accept or drop the message
    //
    MfeaVif *mfea_vif = vif_find_by_vif_index(vif_index);
    if (mfea_vif == NULL)
	return (XORP_ERROR);
    
    //
    // Send the signal to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _kernel_signal_messages_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
	const string& dst_module_instance_name = (*iter).first;
	xorp_module_id dst_module_id = (*iter).second;
	signal_message_send(dst_module_instance_name,
			    dst_module_id,
			    message_type,
			    vif_index,
			    src, dst,
			    rcvbuf,
			    rcvlen);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::signal_dataflow_message_recv:
 * @source: The source address.
 * @group: The group address.
 * @threshold_interval: The dataflow threshold interval.
 * @measured_interval: The dataflow measured interval.
 * @threshold_packets: The threshold (in number of packets) to compare against.
 * @threshold_bytes: The threshold (in number of bytes) to compare against.
 * @measured_packets: The number of packets measured within
 * the @measured_interval.
 * @measured_bytes: The number of bytes measured within
 * the @measured_interval.
 * @is_threshold_in_packets: If true, @threshold_packets is valid.
 * @is_threshold_in_bytes: If true, @threshold_bytes is valid.
 * @is_geq_upcall: If true, the operation for comparison is ">=".
 * @is_leq_upcall: If true, the operation for comparison is "<=".
 * 
 * Process a dataflow upcall from the kernel or from the MFEA internal
 * bandwidth-estimation mechanism (i.e., periodic reading of the kernel
 * multicast forwarding statistics).
 * The signal is sent to all user-level protocols that expect it.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::signal_dataflow_message_recv(const IPvX& source, const IPvX& group,
				       const TimeVal& threshold_interval,
				       const TimeVal& measured_interval,
				       uint32_t threshold_packets,
				       uint32_t threshold_bytes,
				       uint32_t measured_packets,
				       uint32_t measured_bytes,
				       bool is_threshold_in_packets,
				       bool is_threshold_in_bytes,
				       bool is_geq_upcall,
				       bool is_leq_upcall)
{
    XLOG_TRACE(is_log_trace(),
	       "RX dataflow message: "
	       "src = %s dst = %s",
	       cstring(source), cstring(group));
    
    if (! is_up())
	return (XORP_ERROR);
    
    //
    // Send the signal to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _kernel_signal_messages_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
	const string& dst_module_instance_name = (*iter).first;
	xorp_module_id dst_module_id = (*iter).second;
	
	dataflow_signal_send(dst_module_instance_name,
			     dst_module_id,
			     source,
			     group,
			     threshold_interval.sec(),
			     threshold_interval.usec(),
			     measured_interval.sec(),
			     measured_interval.usec(),
			     threshold_packets,
			     threshold_bytes,
			     measured_packets,
			     measured_bytes,
			     is_threshold_in_packets,
			     is_threshold_in_bytes,
			     is_geq_upcall,
			     is_leq_upcall);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::join_multicast_group:
 * @module_instance_name: The module instance name of the protocol to join the
 * multicast group.
 * @module_id: The #xorp_module_id of the protocol to join the multicast
 * group.
 * @vif_index: The vif index of the interface to join.
 * @group: The multicast group to join.
 * 
 * Join a multicast group.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::join_multicast_group(const string& module_instance_name,
			       xorp_module_id module_id,
			       uint32_t vif_index,
			       const IPvX& group)
{
    ProtoComm *proto_comm = proto_comm_find_by_module_id(module_id);
    MfeaVif *mfea_vif = vif_find_by_vif_index(vif_index);
    
    if ((proto_comm == NULL) || (mfea_vif == NULL))
	return (XORP_ERROR);
    
    bool has_group = mfea_vif->has_multicast_group(group);
    
    // Add the state for the group
    if (mfea_vif->add_multicast_group(module_instance_name,
				      module_id,
				      group) < 0) {
	return (XORP_ERROR);
    }
    
    if (! has_group) {
	if (proto_comm->join_multicast_group(vif_index, group) < 0) {
	    mfea_vif->delete_multicast_group(module_instance_name,
					     module_id,
					     group);
	    return (XORP_ERROR);
	}
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::leave_multicast_group:
 * @module_instance_name: The module instance name of the protocol to leave the
 * multicast group.
 * @module_id: The #xorp_module_id of the protocol to leave the multicast
 * group.
 * @vif_index: The vif index of the interface to leave.
 * @group: The multicast group to leave.
 * 
 * Leave a multicast group.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::leave_multicast_group(const string& module_instance_name,
				xorp_module_id module_id,
				uint32_t vif_index,
				const IPvX& group)
{
    ProtoComm *proto_comm = proto_comm_find_by_module_id(module_id);
    MfeaVif *mfea_vif = vif_find_by_vif_index(vif_index);
    
    if ((proto_comm == NULL) || (mfea_vif == NULL))
	return (XORP_ERROR);
    
    // Delete the state for the group
    if (mfea_vif->delete_multicast_group(module_instance_name,
					 module_id,
					 group) < 0) {
	return (XORP_ERROR);
    }
    
    if (! mfea_vif->has_multicast_group(group)) {
	if (proto_comm->leave_multicast_group(vif_index, group) < 0) {
	    return (XORP_ERROR);
	}
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::add_mfc:
 * @module_instance_name: The module instance name of the protocol that adds
 * the MFC.
 * @source: The source address.
 * @group: The group address.
 * @iif_vif_index: The vif index of the incoming interface.
 * @oiflist: The bitset with the outgoing interfaces.
 * @oiflist_disable_wrongvif: The bitset with the outgoing interfaces to
 * disable the WRONGVIF signal.
 * @max_vifs_oiflist: The number of vifs covered by @oiflist
 * or @oiflist_disable_wrongvif.
 * @rp_addr: The RP address.
 * 
 * Add Multicast Forwarding Cache (MFC) to the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::add_mfc(const string& , // module_instance_name,
		  const IPvX& source, const IPvX& group,
		  uint32_t iif_vif_index, const Mifset& oiflist,
		  const Mifset& oiflist_disable_wrongvif,
		  uint32_t max_vifs_oiflist,
		  const IPvX& rp_addr)
{
    uint8_t oifs_ttl[MAX_VIFS];
    uint8_t oifs_flags[MAX_VIFS];
    
    if (max_vifs_oiflist > MAX_VIFS)
	return (XORP_ERROR);
    
    // Check the iif
    if (iif_vif_index == Vif::VIF_INDEX_INVALID)
	return (XORP_ERROR);
    if (iif_vif_index >= max_vifs_oiflist)
	return (XORP_ERROR);
    
    //
    // Reset the initial values
    //
    for (size_t i = 0; i < MAX_VIFS; i++) {
	oifs_ttl[i] = 0;
	oifs_flags[i] = 0;
    }
    
    //
    // Set the minimum required TTL for each outgoing interface,
    // and the optional flags.
    //
    // TODO: XXX: PAVPAVPAV: the TTL should be configurable per vif.
    for (size_t i = 0; i < max_vifs_oiflist; i++) {
	// Set the TTL
	if (oiflist.test(i))
	    oifs_ttl[i] = MINTTL;
	else
	    oifs_ttl[i] = 0;
	
	// Set the flags
	oifs_flags[i] = 0;
	
	if (oiflist_disable_wrongvif.test(i)) {
	    switch (family()) {
	    case AF_INET:
#if defined(MRT_MFC_FLAGS_DISABLE_WRONGVIF) && defined(ENABLE_ADVANCED_MULTICAST_API)
		oifs_flags[i] |= MRT_MFC_FLAGS_DISABLE_WRONGVIF;
#endif
		break;
		
#ifdef HAVE_IPV6
	    case AF_INET6:
	    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
		XLOG_ERROR("add_mfc() failed: "
			   "IPv6 multicast routing not supported");
		return (XORP_ERROR);
#else
#if defined(MRT6_MFC_FLAGS_DISABLE_WRONGVIF) && defined(ENABLE_ADVANCED_MULTICAST_API)
		oifs_flags[i] |= MRT6_MFC_FLAGS_DISABLE_WRONGVIF;
#endif
#endif // HAVE_IPV6_MULTICAST_ROUTING
	    }
	    break;
#endif // HAVE_IPV6
	    
	    default:
		XLOG_UNREACHABLE();
		return (XORP_ERROR);
	    }
	}
    }
    
    if (_mfea_mrouter.add_mfc(source, group, iif_vif_index, oifs_ttl,
			      oifs_flags, rp_addr)
	< 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::delete_mfc:
 * @module_instance_name: The module instance name of the protocol that deletes
 * the MFC.
 * @source: The source address.
 * @group: The group address.
 * 
 * Delete Multicast Forwarding Cache (MFC) from the kernel.
 * XXX: All corresponding dataflow entries are also removed.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::delete_mfc(const string& , // module_instance_name,
		     const IPvX& source, const IPvX& group)
{
    if (_mfea_mrouter.delete_mfc(source, group) < 0) {
	return (XORP_ERROR);
    }
    
    //
    // XXX: Remove all corresponding dataflow entries
    //
    mfea_dft().delete_entry(source, group);
    
    return (XORP_OK);
}

/**
 * MfeaNode::add_dataflow_monitor:
 * @module_instance_name: The module instance name of the protocol that adds
 * the dataflow monitor entry.
 * @source: The source address.
 * @group: The group address.
 * @threshold_interval: The dataflow threshold interval.
 * @threshold_packets: The threshold (in number of packets) to compare against.
 * @threshold_bytes: The threshold (in number of bytes) to compare against.
 * @is_threshold_in_packets: If true, @threshold_packets is valid.
 * @is_threshold_in_bytes: If true, @threshold_bytes is valid.
 * @is_geq_upcall: If true, the operation for comparison is ">=".
 * @is_leq_upcall: If true, the operation for comparison is "<=".
 * @error_msg: The error message (if error).
 * 
 * Add a dataflow monitor entry.
 * Note: either @is_threshold_in_packets or @is_threshold_in_bytes (or both)
 * must be true.
 * Note: either @is_geq_upcall or @is_leq_upcall (but not both) must be true.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::add_dataflow_monitor(const string& ,	// module_instance_name,
			       const IPvX& source, const IPvX& group,
			       const TimeVal& threshold_interval,
			       uint32_t threshold_packets,
			       uint32_t threshold_bytes,
			       bool is_threshold_in_packets,
			       bool is_threshold_in_bytes,
			       bool is_geq_upcall,
			       bool is_leq_upcall,
			       string& error_msg)
{
    // XXX: flags is_geq_upcall and is_leq_upcall are mutually exclusive
    if (! (is_geq_upcall ^ is_leq_upcall)) {
	error_msg = c_format("Cannot add dataflow monitor for (%s, %s): "
			     "the GEQ and LEQ flags are mutually exclusive "
			     "(GEQ = %s; LEQ = %s)",
			     cstring(source), cstring(group),
			     (is_geq_upcall)? "true" : "false",
			     (is_leq_upcall)? "true" : "false");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);		// Invalid arguments
    }
    // XXX: at least one of the threshold flags must be set
    if (! (is_threshold_in_packets || is_threshold_in_bytes)) {
	error_msg = c_format("Cannot add dataflow monitor for (%s, %s): "
			     "invalid threshold flags "
			     "(is_threshold_in_packets = %s; "
			     "is_threshold_in_bytes = %s)",
			     cstring(source), cstring(group),
			     (is_threshold_in_packets)? "true" : "false",
			     (is_threshold_in_bytes)? "true" : "false");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);		// Invalid arguments
    }
    
    //
    // If the kernel supports bandwidth-related upcalls, use it
    //
    if (_mfea_mrouter.mrt_api_mrt_mfc_bw_upcall()) {
	if (_mfea_mrouter.add_bw_upcall(source, group,
					threshold_interval,
					threshold_packets,
					threshold_bytes,
					is_threshold_in_packets,
					is_threshold_in_bytes,
					is_geq_upcall,
					is_leq_upcall,
					error_msg)
	    < 0) {
	    return (XORP_ERROR);
	}
	return (XORP_OK);
    }
    
    //
    // The kernel doesn't support bandwidth-related upcalls, hence use
    // a work-around mechanism (periodic quering).
    //
    if (mfea_dft().add_entry(source, group,
			     threshold_interval,
			     threshold_packets,
			     threshold_bytes,
			     is_threshold_in_packets,
			     is_threshold_in_bytes,
			     is_geq_upcall,
			     is_leq_upcall,
			     error_msg)
	< 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::delete_dataflow_monitor:
 * @module_instance_name: The module instance name of the protocol that deletes
 * the dataflow monitor entry.
 * @source: The source address.
 * @group: The group address.
 * @threshold_interval: The dataflow threshold interval.
 * @threshold_packets: The threshold (in number of packets) to compare against.
 * @threshold_bytes: The threshold (in number of bytes) to compare against.
 * @is_threshold_in_packets: If true, @threshold_packets is valid.
 * @is_threshold_in_bytes: If true, @threshold_bytes is valid.
 * @is_geq_upcall: If true, the operation for comparison is ">=".
 * @is_leq_upcall: If true, the operation for comparison is "<=".
 * @error_msg: The error message (if error).
 * 
 * Delete a dataflow monitor entry.
 * Note: either @is_threshold_in_packets or @is_threshold_in_bytes (or both)
 * must be true.
 * Note: either @is_geq_upcall or @is_leq_upcall (but not both) must be true.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::delete_dataflow_monitor(const string& , // module_instance_name,
				  const IPvX& source, const IPvX& group,
				  const TimeVal& threshold_interval,
				  uint32_t threshold_packets,
				  uint32_t threshold_bytes,
				  bool is_threshold_in_packets,
				  bool is_threshold_in_bytes,
				  bool is_geq_upcall,
				  bool is_leq_upcall,
				  string& error_msg)
{
    // XXX: flags is_geq_upcall and is_leq_upcall are mutually exclusive
    if (! (is_geq_upcall ^ is_leq_upcall)) {
	error_msg = c_format("Cannot delete dataflow monitor for (%s, %s): "
			     "the GEQ and LEQ flags are mutually exclusive "
			     "(GEQ = %s; LEQ = %s)",
			     cstring(source), cstring(group),
			     (is_geq_upcall)? "true" : "false",
			     (is_leq_upcall)? "true" : "false");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);		// Invalid arguments
    }
    // XXX: at least one of the threshold flags must be set
    if (! (is_threshold_in_packets || is_threshold_in_bytes)) {
	error_msg = c_format("Cannot delete dataflow monitor for (%s, %s): "
			     "invalid threshold flags "
			     "(is_threshold_in_packets = %s; "
			     "is_threshold_in_bytes = %s)",
			     cstring(source), cstring(group),
			     (is_threshold_in_packets)? "true" : "false",
			     (is_threshold_in_bytes)? "true" : "false");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);		// Invalid arguments
    }
    
    //
    // If the kernel supports bandwidth-related upcalls, use it
    //
    if (_mfea_mrouter.mrt_api_mrt_mfc_bw_upcall()) {
	if (_mfea_mrouter.delete_bw_upcall(source, group,
					   threshold_interval,
					   threshold_packets,
					   threshold_bytes,
					   is_threshold_in_packets,
					   is_threshold_in_bytes,
					   is_geq_upcall,
					   is_leq_upcall,
					   error_msg)
	    < 0) {
	    return (XORP_ERROR);
	}
	return (XORP_OK);
    }
    
    //
    // The kernel doesn't support bandwidth-related upcalls, hence use
    // a work-around mechanism (periodic quering).
    //
    if (mfea_dft().delete_entry(source, group,
				threshold_interval,
				threshold_packets,
				threshold_bytes,
				is_threshold_in_packets,
				is_threshold_in_bytes,
				is_geq_upcall,
				is_leq_upcall,
				error_msg)
	< 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::delete_all_dataflow_monitor:
 * @module_instance_name: The module instance name of the protocol that deletes
 * the dataflow monitor entry.
 * @source: The source address.
 * @group: The group address.
 * @error_msg: The error message (if error).
 * 
 * Delete all dataflow monitor entries for a given @source and @group address.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::delete_all_dataflow_monitor(const string& , // module_instance_name,
				      const IPvX& source, const IPvX& group,
				      string& error_msg)
{
    //
    // If the kernel supports bandwidth-related upcalls, use it
    //
    if (_mfea_mrouter.mrt_api_mrt_mfc_bw_upcall()) {
	if (_mfea_mrouter.delete_all_bw_upcall(source, group, error_msg) < 0) {
	    return (XORP_ERROR);
	}
	return (XORP_OK);
    }
    
    //
    // The kernel doesn't support bandwidth-related upcalls, hence use
    // a work-around mechanism (periodic quering).
    //
    if (mfea_dft().delete_entry(source, group) < 0) {
	error_msg = c_format("Cannot delete dataflow monitor for (%s, %s): "
			     "no such entry",
			     cstring(source), cstring(group));
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::add_multicast_vif:
 * @vif_index: The vif index of the interface to add.
 * 
 * Add a multicast vif to the kernel.
 * 
 * Return value: %XORP_OK on success, othewise %XORP_ERROR.
 **/
int
MfeaNode::add_multicast_vif(uint32_t vif_index)
{
    if (_mfea_mrouter.add_multicast_vif(vif_index) < 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::delete_multicast_vif:
 * @vif_index: The vif index of the interface to delete.
 * 
 * Delete a multicast vif from the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::delete_multicast_vif(uint32_t vif_index)
{
    if (_mfea_mrouter.delete_multicast_vif(vif_index) < 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::get_sg_count:
 * @source: The MFC source address.
 * @group: The MFC group address.
 * @sg_count: A reference to a #SgCount class to place the result: the
 * number of packets and bytes forwarded by the particular MFC entry, and the
 * number of packets arrived on a wrong interface.
 * 
 * Get the number of packets and bytes forwarded by a particular
 * Multicast Forwarding Cache (MFC) entry in the kernel, and the number
 * of packets arrived on wrong interface for that entry.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::get_sg_count(const IPvX& source, const IPvX& group,
		       SgCount& sg_count)
{
    if (_mfea_mrouter.get_sg_count(source, group, sg_count) < 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::get_vif_count:
 * @vif_index: The vif index of the virtual multicast interface whose
 * statistics we need.
 * @vif_count: A reference to a #VifCount class to store the result.
 * 
 * Get the number of packets and bytes received on, or forwarded on
 * a particular multicast interface.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaNode::get_vif_count(uint32_t vif_index, VifCount& vif_count)
{
    if (_mfea_mrouter.get_vif_count(vif_index, vif_count) < 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaNode::proto_comm_find_by_module_id:
 * @module_id: The #xorp_module_id to search for.
 * 
 * Return the #ProtoComm entry that corresponds to @module_id.
 * 
 * Return value: The corresponding #ProtoComm entry if found, otherwise NULL.
 **/
ProtoComm *
MfeaNode::proto_comm_find_by_module_id(xorp_module_id module_id) const
{
    for (size_t i = 0; i < _proto_comms.size(); i++) {
	if (_proto_comms[i] != NULL) {
	    if (_proto_comms[i]->module_id() == module_id)
		return (_proto_comms[i]);
	}
    }
    
    return (NULL);
}

/**
 * MfeaNode::proto_comm_find_by_ip_protocol:
 * @ip_protocol: The IP protocol number to search for.
 * 
 * Return the #ProtoComm entry that corresponds to @ip_protocol IP protocol
 * number.
 * 
 * Return value: The corresponding #ProtoComm entry if found, otherwise NULL.
 **/
ProtoComm *
MfeaNode::proto_comm_find_by_ip_protocol(int ip_protocol) const
{
    for (size_t i = 0; i < _proto_comms.size(); i++) {
	if (_proto_comms[i] != NULL) {
	    if (_proto_comms[i]->ip_protocol() == ip_protocol)
		return (_proto_comms[i]);
	}
    }
    
    return (NULL);
}


syntax highlighted by Code2HTML, v. 0.9.1