// -*- 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_mrouter.cc,v 1.50 2007/02/16 22:45:45 pavlin Exp $"

//
// Multicast routing kernel-access specific 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"

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_NET_IF_VAR_H
#include <net/if_var.h>
#endif
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_IP6_H
#include <netinet/ip6.h>
#endif
#ifdef HAVE_NETINET_ICMP6_H
#include <netinet/icmp6.h>
#endif
#ifdef HAVE_NETINET6_IN6_VAR_H
#include <netinet6/in6_var.h>
#endif

#include "libcomm/comm_api.h"

#include "libproto/packet.hh"

#include "mrt/include/ip_mroute.h"
#include "mrt/max_vifs.h"
#include "mrt/multicast_defs.h"

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


//
// Exported variables
//

//
// Static class members
//

//
// Local constants definitions
//
#define IO_BUF_SIZE		(64*1024)  // I/O buffer(s) size
#define CMSG_BUF_SIZE		(10*1024)  // 'rcvcmsgbuf' and 'sndcmsgbuf'
#define SO_RCV_BUF_SIZE_MIN	(48*1024)  // Min. socket buffer size
#define SO_RCV_BUF_SIZE_MAX	(256*1024) // Desired socket buffer size

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

#ifdef HOST_OS_WINDOWS
typedef char *caddr_t;
#endif

//
// Local variables
//

//
// Local functions prototypes
//

/**
 * MfeaMrouter::MfeaMrouter:
 * @mfea_node: The MfeaNode I belong to.
 **/
MfeaMrouter::MfeaMrouter(MfeaNode& mfea_node)
    : ProtoUnit(mfea_node.family(), mfea_node.module_id()),
      _mfea_node(mfea_node)
{
    // Allocate the buffers
    _rcvbuf0 = new uint8_t[IO_BUF_SIZE];
    _sndbuf0 = new uint8_t[IO_BUF_SIZE];
    _rcvbuf1 = new uint8_t[IO_BUF_SIZE];
    _sndbuf1 = new uint8_t[IO_BUF_SIZE];
    _rcvcmsgbuf = new uint8_t[CMSG_BUF_SIZE];
    _sndcmsgbuf = new uint8_t[CMSG_BUF_SIZE];

    // Scatter/gatter array initialization
    _rcviov[0].iov_base		= (caddr_t)_rcvbuf0;
    _rcviov[1].iov_base		= (caddr_t)_rcvbuf1;
    _rcviov[0].iov_len		= IO_BUF_SIZE;
    _rcviov[1].iov_len		= IO_BUF_SIZE;
    _sndiov[0].iov_base		= (caddr_t)_sndbuf0;
    _sndiov[1].iov_base		= (caddr_t)_sndbuf1;
    _sndiov[0].iov_len		= 0;
    _sndiov[1].iov_len		= 0;

    // recvmsg() and sendmsg() related initialization
#ifndef HOST_OS_WINDOWS
    switch (family()) {
    case AF_INET:
	_rcvmh.msg_name		= (caddr_t)&_from4;
	_sndmh.msg_name		= (caddr_t)&_to4;
	_rcvmh.msg_namelen	= sizeof(_from4);
	_sndmh.msg_namelen	= sizeof(_to4);
	break;
#ifdef HAVE_IPV6
    case AF_INET6:
	_rcvmh.msg_name		= (caddr_t)&_from6;
	_sndmh.msg_name		= (caddr_t)&_to6;
	_rcvmh.msg_namelen	= sizeof(_from6);
	_sndmh.msg_namelen	= sizeof(_to6);
	break;
#endif // HAVE_IPV6
    default:
	XLOG_UNREACHABLE();
	break;
    }
    _rcvmh.msg_iov		= _rcviov;
    _sndmh.msg_iov		= _sndiov;
    _rcvmh.msg_iovlen		= 1;
    _sndmh.msg_iovlen		= 1;
    _rcvmh.msg_control		= (caddr_t)_rcvcmsgbuf;
    _sndmh.msg_control		= (caddr_t)_sndcmsgbuf;
    _rcvmh.msg_controllen	= CMSG_BUF_SIZE;
    _sndmh.msg_controllen	= 0;
#endif // ! HOST_OS_WINDOWS

    _mrt_api_mrt_mfc_flags_disable_wrongvif = false;
    _mrt_api_mrt_mfc_flags_border_vif = false;
    _mrt_api_mrt_mfc_rp = false;
    _mrt_api_mrt_mfc_bw_upcall = false;
}

MfeaMrouter::~MfeaMrouter()
{
    stop();
    
    // Free the buffers
    delete[] _rcvbuf0;
    delete[] _sndbuf0;
    delete[] _rcvbuf1;
    delete[] _sndbuf1;
    delete[] _rcvcmsgbuf;
    delete[] _sndcmsgbuf;
}

/**
 * MfeaMrouter::start:
 * @: 
 * 
 * Start the MfeaMrouter.
 * 
 * Return value: %XORP_OK on success, otherwize %XORP_ERROR.
 **/
int
MfeaMrouter::start()
{
#ifdef HOST_OS_WINDOWS
    XLOG_ERROR("Multicast routing is not supported on Windows");
    return (XORP_ERROR);
#endif

    // XXX: MfeaMrouter is automatically enabled by default
    ProtoUnit::enable();
    
    if (is_up() || is_pending_up())
	return (XORP_OK);

    if (ProtoUnit::start() < 0)
	return (XORP_ERROR);
    
#ifndef HOST_OS_WINDOWS
    // Check if we have the necessary permission
    if (geteuid() != 0) {
	XLOG_ERROR("Must be root");
	exit (1);
	// return (XORP_ERROR);
    }
#endif // ! HOST_OS_WINDOWS
    
    // Open kernel multicast routing access socket
    if (!open_mrouter_socket().is_valid())
	return (XORP_ERROR);
    
    // Start the multicast routing in the kernel
    if (start_mrt() < 0)
	return (XORP_ERROR);
    
    return (XORP_OK);
}

/**
 * MfeaMrouter::stop:
 * @: 
 * 
 * Stop the MfeaMrouter.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::stop()
{
    if (is_down())
	return (XORP_OK);

    if (ProtoUnit::stop() < 0)
	return (XORP_ERROR);
    
    // Stop the multicast routing in the kernel
    stop_mrt();
    
    // Close kernel multicast routing access socket
    close_mrouter_socket();
    
    return (XORP_OK);
}

/**
 * Test if the underlying system supports IPv4 multicast routing.
 * 
 * @return true if the underlying system supports IPv4 multicast routing,
 * otherwise false.
 */
bool
MfeaMrouter::have_multicast_routing4() const
{
#ifndef HAVE_IPV4_MULTICAST_ROUTING
    return (false);
#else
    int s;
    int mrouter_version = 1;	// XXX: hardcoded version
    
    if (! is_ipv4())
	return (false);		// Wrong family
    
    //
    // Test to open and initialize a mrouter socket. If success,
    // then we support multicast routing.
    //
    if (mrouter_socket() >= 0)
	return (true);		// XXX: already have an open mrouter socket
    
    if (kernel_mrouter_ip_protocol() < 0)
	return (false);
    
    s = socket(family(), SOCK_RAW, kernel_mrouter_ip_protocol());
    if (s < 0)
	return (false);		// Failure to open the socket
    
    if (setsockopt(s, IPPROTO_IP, MRT_INIT,
		   (void *)&mrouter_version, sizeof(mrouter_version))
	< 0) {
	close(s);
	return (false);
    }
    
    // Success
    close(s);
    return (true);
#endif // HAVE_IPV4_MULTICAST_ROUTING
}

/**
 * Test if the underlying system supports IPv6 multicast routing.
 * 
 * @return true if the underlying system supports IPv6 multicast routing,
 * otherwise false.
 */
bool
MfeaMrouter::have_multicast_routing6() const
{
#ifndef HAVE_IPV6_MULTICAST_ROUTING
    return (false);
#else
    int s;
    int mrouter_version = 1;	// XXX: hardcoded version
    
    if (! is_ipv6())
	return (false);		// Wrong family
    
    //
    // Test to open and initialize a mrouter socket. If success,
    // then we support multicast routing.
    //
    if (mrouter_socket() >= 0)
	return (true);		// XXX: already have an open mrouter socket
    
    if (kernel_mrouter_ip_protocol() < 0)
	return (false);
    
    s = socket(family(), SOCK_RAW, kernel_mrouter_ip_protocol());
    if (s < 0)
	return (false);		// Failure to open the socket
    
    if (setsockopt(s, IPPROTO_IPV6, MRT6_INIT,
		   (void *)&mrouter_version, sizeof(mrouter_version))
	< 0) {
	close(s);
	return (false);
    }
    
    // Success
    close(s);
    return (true);
#endif // HAVE_IPV6_MULTICAST_ROUTING
}

/**
 * MfeaMrouter::kernel_mrouter_ip_protocol:
 * @: 
 * 
 * Get the protocol that would be used in case of mrouter socket.
 * 
 * Return value: the protocol number on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::kernel_mrouter_ip_protocol() const
{
    switch (family()) {
    case AF_INET:
	return (IPPROTO_IGMP);
#ifdef HAVE_IPV6
    case AF_INET6:
	return (IPPROTO_ICMPV6);
#endif // HAVE_IPV6
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_ERROR);
}

/**
 * MfeaMrouter::open_mrouter_socket:
 * @: 
 * 
 * Open the mrouter socket (used for various multicast routing kernel calls).
 * 
 * Return value: The socket value.
 **/
XorpFd
MfeaMrouter::open_mrouter_socket()
{
    XorpFd badfd;

    if (_mrouter_socket.is_valid())
	return (_mrouter_socket);

    if (kernel_mrouter_ip_protocol() < 0)
	return (badfd);
    
    //
    // XXX: if we have already IGMP or ICMPV6 socket, then reuse it
    //
    do {
	ProtoComm *proto_comm;
	proto_comm = mfea_node().proto_comm_find_by_ip_protocol(kernel_mrouter_ip_protocol());
	if (proto_comm == NULL)
	    break;
	_mrouter_socket = proto_comm->proto_socket_in();
	if (_mrouter_socket.is_valid())
	    return (_mrouter_socket);
	break;
    } while (false);
    
    _mrouter_socket = socket(family(), SOCK_RAW, kernel_mrouter_ip_protocol());
    if (!_mrouter_socket.is_valid()) {
	XLOG_ERROR("Cannot open mrouter socket");
	return (badfd);
    }
    
    if (!adopt_mrouter_socket().is_valid())
	return (badfd);
    
    return (_mrouter_socket);
}

/**
 * MfeaMrouter::adopt_mrouter_socket:
 * @: 
 * 
 * Adopt control over the mrouter socket.
 * When the #MfeaMrouter adopts control over the mrouter socket,
 * it is the one that will be reading from that socket.
 * 
 * Return value: The adopted socket value.
 **/
XorpFd
MfeaMrouter::adopt_mrouter_socket()
{
    XorpFd badfd;

    if (!_mrouter_socket.is_valid())
	return (badfd);
    
    XLOG_ASSERT(is_up());
    
    // Set receiving buffer size
    if (comm_sock_set_rcvbuf(_mrouter_socket, SO_RCV_BUF_SIZE_MAX,
			     SO_RCV_BUF_SIZE_MIN)
	< SO_RCV_BUF_SIZE_MIN) {
	comm_close(_mrouter_socket);
	_mrouter_socket.clear();
	return (badfd);
    }
    
    // Protocol-specific setup
    switch (family()) {
    case AF_INET:
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("adopt_mrouter_socket() failed: "
		   "IPv4 multicast routing not supported");
	return (badfd);
#endif
	break;
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("adopt_mrouter_socket() failed: "
		   "IPv6 multicast routing not supported");
	return (badfd);
#else
	struct icmp6_filter filter;
	// Filter all ICMPv6 messages
	ICMP6_FILTER_SETBLOCKALL(&filter);
	if (setsockopt(_mrouter_socket, IPPROTO_ICMPV6, ICMP6_FILTER,
		       (void *)&filter, sizeof(filter)) < 0) {
	    XLOG_ERROR("setsockopt(ICMP6_FILTER) failed: %s", strerror(errno));
	    comm_close(_mrouter_socket);
	    _mrouter_socket.clear();
	    return (badfd);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    default:
	XLOG_UNREACHABLE();
	return (badfd);
    }
    
    // Assign a method to read from this socket
    if (mfea_node().eventloop().add_ioevent_cb(_mrouter_socket, IOT_READ,
				callback(this,
					 &MfeaMrouter::mrouter_socket_read))
	== false) {
	XLOG_ERROR("Cannot add mrouter socket to the set of sockets "
		   "to read from in the event loop");
	comm_close(_mrouter_socket);
	_mrouter_socket.clear();
	return (badfd);
    }
    
    return (_mrouter_socket);
}

/**
 * MfeaMrouter::close_mrouter_socket:
 * @: 
 * 
 * Close the mrouter socket.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::close_mrouter_socket()
{
    if (!_mrouter_socket.is_valid())
	return (XORP_ERROR);
    
    if (kernel_mrouter_ip_protocol() < 0)
	return (XORP_ERROR);
    
    //
    // XXX: if we have already IGMP or ICMPV6 socket, then we must be
    // using the same socket, hence don't close it.
    //
    do {
	ProtoComm *proto_comm;
	proto_comm = mfea_node().proto_comm_find_by_ip_protocol(kernel_mrouter_ip_protocol());
	if (proto_comm == NULL)
	    break;
	// If there is already IGMP or ICMPV6 socket, then don't close it.
	if (_mrouter_socket == proto_comm->proto_socket_in()) {
	    // Transfer the I/O event callback to the protocol socket
	    mfea_node().eventloop().remove_ioevent_cb(_mrouter_socket);
	    proto_comm->add_proto_socket_in_callback();
	    _mrouter_socket.clear();
	    return (XORP_OK);
	}
	break;
    } while (false);
    
    // Remove the function for reading from this socket    
    mfea_node().eventloop().remove_ioevent_cb(_mrouter_socket);
    
    // Close the socket and set its handle to an invalid value.
    if (comm_close(_mrouter_socket) == XORP_ERROR) {
	XLOG_ERROR("Cannot close mrouter socket: %s",
		    comm_get_last_error_str());
	_mrouter_socket.clear();
	return (XORP_ERROR);
    }
    
    _mrouter_socket.clear();
    
    return (XORP_OK);
}

/**
 * MfeaMrouter::start_mrt:
 * @: 
 * 
 * Start/enable the multicast routing in the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::start_mrt()
{
    int mrouter_version = 1;	// XXX: hardcoded version
#if !defined(HAVE_IPV4_MULTICAST_ROUTING) && !defined(HAVE_IPV6_MULTICAST_ROUTING)
    UNUSED(mrouter_version)
#endif
    
    switch (family()) {
    case AF_INET:
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("start_mrt() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_INIT,
		       (void *)&mrouter_version, sizeof(mrouter_version))
	    < 0) {
	    XLOG_ERROR("setsockopt(MRT_INIT, %u) failed: %s",
		       mrouter_version, strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
	break;

#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("start_mrt() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_INIT,
		       (void *)&mrouter_version, sizeof(mrouter_version))
	    < 0) {
	    XLOG_ERROR("setsockopt(MRT6_INIT, %u) failed: %s",
		       mrouter_version, strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6

    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    //
    // Configure advanced multicast API
    //
#if defined(MRT_API_CONFIG) && defined(ENABLE_ADVANCED_MULTICAST_API)
    if (family() == AF_INET) {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("start_mrt() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	uint32_t mrt_api = 0;
	
	//
	// Set the desired API
	//
#ifdef MRT_MFC_FLAGS_DISABLE_WRONGVIF
	// Try to enable the support for disabling WRONGVIF signals per vif
	mrt_api |= MRT_MFC_FLAGS_DISABLE_WRONGVIF;
#endif
#ifdef MRT_MFC_FLAGS_BORDER_VIF
	// Try to enable the border bit flag (per MFC per vif)
	mrt_api |= MRT_MFC_FLAGS_BORDER_VIF;
#endif
#ifdef MRT_MFC_RP
	// Try to enable kernel-level PIM Register encapsulation
	mrt_api |= MRT_MFC_RP;
#endif
#ifdef MRT_MFC_BW_UPCALL
	// Try to enable bandwidth-related upcalls from the kernel
	mrt_api |= MRT_MFC_BW_UPCALL;
#endif
	
	//
	// Try to configure the kernel with the desired API
	//
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_API_CONFIG,
		       (void *)&mrt_api, sizeof(mrt_api)) < 0) {
	    XLOG_ERROR("setsockopt(MRT_API_CONFIG) failed: %s",
		       strerror(errno));
	    return (XORP_ERROR);
	}
	
	//
	// Test which of the desired API support succeded
	//
#ifdef MRT_MFC_FLAGS_DISABLE_WRONGVIF
	// Test the support for disabling WRONGVIF signals per vif
	if (mrt_api & MRT_MFC_FLAGS_DISABLE_WRONGVIF)
	    _mrt_api_mrt_mfc_flags_disable_wrongvif = true;
#endif
#ifdef MRT_MFC_FLAGS_BORDER_VIF
	// Test the support for the border bit flag (per MFC per vif)
	if (mrt_api & MRT_MFC_FLAGS_BORDER_VIF)
	    _mrt_api_mrt_mfc_flags_border_vif = true;
#endif
#ifdef MRT_MFC_RP
	// Test the support for kernel-level PIM Register encapsulation
	if (mrt_api & MRT_MFC_RP)
	    _mrt_api_mrt_mfc_rp = true;
#endif
#ifdef MRT_MFC_BW_UPCALL
	// Test the support for bandwidth-related upcalls from the kernel
	if (mrt_api & MRT_MFC_BW_UPCALL)
	    _mrt_api_mrt_mfc_bw_upcall = true;
#endif

#endif // HAVE_IPV4_MULTICAST_ROUTING	
	
    }
#endif // MRT_API_CONFIG && ENABLE_ADVANCED_MULTICAST_API
    
#if defined(MRT6_API_CONFIG) && defined(ENABLE_ADVANCED_MULTICAST_API)
#ifdef HAVE_IPV6
    if (family == AF_INET6) {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("start_mrt() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	
	uint32_t mrt_api = 0;
	
	//
	// Set the desired API
	//
#ifdef MRT6_MFC_FLAGS_DISABLE_WRONGVIF
	// Try to enable the support for disabling WRONGVIF signals per vif
	mrt_api |= MRT6_MFC_FLAGS_DISABLE_WRONGVIF;
#endif
#ifdef MRT6_MFC_FLAGS_BORDER_VIF
	// Try to enable the border bit flag (per MFC per vif)
	mrt_api |= MRT6_MFC_FLAGS_BORDER_VIF;
#endif
#ifdef MRT6_MFC_RP
	// Try to enable kernel-level PIM Register encapsulation
	mrt_api |= MRT6_MFC_RP;
#endif
#ifdef MRT6_MFC_BW_UPCALL
	// Try to enable bandwidth-related upcalls from the kernel
	mrt_api |= MRT6_MFC_BW_UPCALL;
#endif
	
	//
	// Try to configure the kernel with the desired API
	//
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT6_API_CONFIG,
		       (void *)&mrt_api, sizeof(mrt_api)) < 0) {
	    XLOG_ERROR("setsockopt(MRT6_API_CONFIG) failed: %s",
		       strerror(errno));
	    return (XORP_ERROR);
	}
	
	//
	// Test which of the desired API support succeded
	//
#ifdef MRT6_MFC_FLAGS_DISABLE_WRONGVIF
	// Test the support for disabling WRONGVIF signals per vif
	if (mrt_api & MRT6_MFC_FLAGS_DISABLE_WRONGVIF)
	    _mrt_api_mrt_mfc_flags_disable_wrongvif = true;
#endif
#ifdef MRT6_MFC_FLAGS_BORDER_VIF
	// Test the support for the border bit flag (per MFC per vif)
	if (mrt_api & MRT6_MFC_FLAGS_BORDER_VIF)
	    _mrt_api_mrt_mfc_flags_border_vif = true;
#endif
#ifdef MRT6_MFC_RP
	// Test the support for kernel-level PIM Register encapsulation
	if (mrt_api & MRT6_MFC_RP)
	    _mrt_api_mrt_mfc_rp = true;
#endif
#ifdef MRT6_MFC_BW_UPCALL
	// Test the support for bandwidth-related upcalls from the kernel
	if (mrt_api & MRT6_MFC_BW_UPCALL)
	    _mrt_api_mrt_mfc_bw_upcall = true;
#endif
	
#endif // HAVE_IPV6_MULTICAST_ROUTING	
    }
#endif // HAVE_IPV6
#endif // MRT6_API_CONFIG && ENABLE_ADVANCED_MULTICAST_API
    
    return (XORP_OK);
}


/**
 * MfeaMrouter::stop_mrt:
 * @: 
 * 
 * Stop/disable the multicast routing in the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::stop_mrt()
{
    _mrt_api_mrt_mfc_flags_disable_wrongvif = false;
    _mrt_api_mrt_mfc_flags_border_vif = false;
    _mrt_api_mrt_mfc_rp = false;
    _mrt_api_mrt_mfc_bw_upcall = false;
    
    if (!_mrouter_socket.is_valid())
	return (XORP_ERROR);
    
    switch (family()) {
    case AF_INET:
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("stop_mrt() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DONE, NULL, 0)
	    < 0) {
	    XLOG_ERROR("setsockopt(MRT_DONE) failed: %s", strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
	break;
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("stop_mrt() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_DONE, NULL, 0)
	    < 0) {
	    XLOG_ERROR("setsockopt(MRT6_DONE) failed: %s", strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}


/**
 * MfeaMrouter::start_pim:
 * @: 
 * 
 * Start/enable PIM routing in the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::start_pim()
{
    int v = 1;

    switch (family()) {
    case AF_INET:
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("start_pim() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_PIM,
		       (void *)&v, sizeof(v)) < 0) {
	    XLOG_ERROR("setsockopt(MRT_PIM, %u) failed: %s",
		       v, strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
	break;

#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("start_pim() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_PIM,
		       (void *)&v, sizeof(v)) < 0) {
	    XLOG_ERROR("setsockopt(MRT6_PIM, %u) failed: %s",
		       v, strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6

    default:
	UNUSED(v);
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }

    return (XORP_OK);
}


/**
 * MfeaMrouter::stop_pim:
 * @: 
 * 
 * Stop/disable PIM routing in the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::stop_pim()
{
    int v = 0;

    if (!_mrouter_socket.is_valid())
	return (XORP_ERROR);
    
    switch (family()) {
    case AF_INET:
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("stop_pim() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	v = 0;
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_PIM,
		       (void *)&v, sizeof(v)) < 0) {
	    XLOG_ERROR("setsockopt(MRT_PIM, %u) failed: %s",
		       v, strerror(errno));
	    return (XORP_ERROR);
	}
#endif
	break;

#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("stop_pim() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	v = 0;
	if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_PIM,
		       (void *)&v, sizeof(v)) < 0) {
	    XLOG_ERROR("setsockopt(MRT6_PIM, %u) failed: %s",
		       v, strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6

    default:
	UNUSED(v);
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }

    return (XORP_OK);
}

/**
 * MfeaMrouter::add_multicast_vif:
 * @vif_index: The vif index of the virtual interface to add.
 * 
 * Add a virtual multicast interface to the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::add_multicast_vif(uint32_t vif_index)
{
    MfeaVif *mfea_vif = mfea_node().vif_find_by_vif_index(vif_index);
    
    if (mfea_vif == NULL)
	return (XORP_ERROR);
    
    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("add_multicast_vif() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct vifctl vc;
	
	memset(&vc, 0, sizeof(vc));
	vc.vifc_vifi = mfea_vif->vif_index();
	// XXX: we don't (need to) support VIFF_TUNNEL; VIFF_SRCRT is obsolete
	vc.vifc_flags = 0;
	if (mfea_vif->is_pim_register())
	    vc.vifc_flags	|= VIFF_REGISTER;
	vc.vifc_threshold	= mfea_vif->min_ttl_threshold();
	vc.vifc_rate_limit	= mfea_vif->max_rate_limit();
	
	if (mfea_vif->addr_ptr() == NULL) {
	    XLOG_ERROR("add_multicast_vif() failed: vif %s has no address",
		       mfea_vif->name().c_str());
	    return (XORP_ERROR);
	}
	mfea_vif->addr_ptr()->copy_out(vc.vifc_lcl_addr);
	//
	// XXX: no need to copy any remote address to vc.vifc_rmt_addr,
	// because we don't (need to) support IPIP tunnels.
	//
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_ADD_VIF,
		       (void *)&vc, sizeof(vc)) < 0) {
	    XLOG_ERROR("setsockopt(MRT_ADD_VIF, vif %s) failed: %s",
		       mfea_vif->name().c_str(), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("add_multicast_vif() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct mif6ctl mc;
	
	memset(&mc, 0, sizeof(mc));
	mc.mif6c_mifi = mfea_vif->vif_index();
	mc.mif6c_flags = 0;
	if (mfea_vif->is_pim_register())
	    mc.mif6c_flags |= MIFF_REGISTER;
	mc.mif6c_pifi = mfea_vif->pif_index();
#if 0		// TODO: enable it after KAME's stack defines it
	mc.mif6c_threshold = mfea_vif->min_ttl_threshold();
	mc.mif6c_rate_limit = mfea_vif->max_rate_limit();
#endif // 0
	if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_ADD_MIF,
		       (void *)&mc, sizeof(mc)) < 0) {
	    XLOG_ERROR("setsockopt(MRT6_ADD_MIF, vif %s) failed: %s",
		       mfea_vif->name().c_str(), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}


/**
 * MfeaMrouter::delete_multicast_vif:
 * @vif_index: The vif index of the interface to delete.
 * 
 * Delete a virtual multicast interface from the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::delete_multicast_vif(uint32_t vif_index)
{
    MfeaVif *mfea_vif = mfea_node().vif_find_by_vif_index(vif_index);
    
    if (mfea_vif == NULL)
	return (XORP_ERROR);
    
    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("delete_multicast_vif() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	int ret_value = -1;

	//
	// XXX: In case of Linux, MRT_DEL_VIF expects an argument
	// of type "struct vifctl", while other systems expect
	// an argument of type "vifi_t".
	//
#ifdef HOST_OS_LINUX
	struct vifctl vc;
	memset(&vc, 0, sizeof(vc));
	vc.vifc_vifi = mfea_vif->vif_index();
	ret_value = setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_VIF,
			       (void *)&vc, sizeof(vc));
#else
	vifi_t vifi = mfea_vif->vif_index();
	ret_value = setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_VIF,
			       (void *)&vifi, sizeof(vifi));
#endif

	if (ret_value < 0) {
	    XLOG_ERROR("setsockopt(MRT_DEL_VIF, vif %s) failed: %s",
		       mfea_vif->name().c_str(), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("delete_multicast_vif() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	int ret_value = -1;

	//
	// XXX: In case of Linux, MRT_DEL_VIF expects an argument
	// of type "struct vifctl", while other systems expect
	// an argument of type "vifi_t".
	//
	// TODO: note that currently (2004/06/09) Linux doesn't support
	// IPv6 multicast routing, hence the above is a guess based on
	// the difference in case of IPv4.
	//
#ifdef HOST_OS_LINUX
	struct mif6ctl mc;
	memset(&mc, 0, sizeof(mc));
	mc.mif6c_mifi = mfea_vif->vif_index();
	mc.mif6c_pifi = mfea_vif->pif_index();
	ret_value = setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_VIF,
			       (void *)&mc, sizeof(mc));
#else
	mifi_t vifi = mfea_vif->vif_index();
	ret_value = setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_DEL_MIF,
			       (void *)&vifi, sizeof(vifi));
#endif
	if (ret_value < 0) {
	    XLOG_ERROR("setsockopt(MRT6_DEL_MIF, vif %s) failed: %s",
		       mfea_vif->name().c_str(), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}


/**
 * MfeaMrouter::add_mfc:
 * @source: The MFC source address.
 * @group: The MFC group address.
 * @iif_vif_index: The MFC incoming interface index.
 * @oifs_ttl: An array with the min. TTL a packet should have to be forwarded.
 * @oifs_flags: An array with misc. flags for the MFC to install.
 * Note that those flags are supported only by the advanced multicast API.
 * @rp_addr: The RP address.
 * 
 * Install/modify a Multicast Forwarding Cache (MFC) entry in the kernel.
 * If the MFC entry specified by (@source, @group) pair was not
 * installed before, a new MFC entry will be created in the kernel;
 * otherwise, the existing entry's fields will be modified.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::add_mfc(const IPvX& source, const IPvX& group,
		     uint32_t iif_vif_index, uint8_t *oifs_ttl,
		     uint8_t *oifs_flags,
		     const IPvX& rp_addr)
{
    if (iif_vif_index >= mfea_node().maxvifs())
	return (XORP_ERROR);
    
    oifs_ttl[iif_vif_index] = 0;		// Pre-caution

#if !defined(HAVE_IPV4_MULTICAST_ROUTING) && !defined(HAVE_IPV6_MULTICAST_ROUTING)
    UNUSED(source);
    UNUSED(group);
#endif

    UNUSED(oifs_flags);
    UNUSED(rp_addr);

    if (mfea_node().is_log_trace()) {
	string res;
	for (uint32_t i = 0; i < mfea_node().maxvifs(); i++) {
	    if (oifs_ttl[i] > 0)
		res += "O";
	    else
		res += ".";
	}
	XLOG_TRACE(mfea_node().is_log_trace(),
		   "Add MFC entry: (%s, %s) iif = %d olist = %s",
		   cstring(source),
		   cstring(group),
		   iif_vif_index,
		   res.c_str());
    }

    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("add_mfc() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else

#if defined(HAVE_MFCCTL2) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct mfcctl2 mc;
#else
	struct mfcctl mc;
#endif
	
	memset(&mc, 0, sizeof(mc));
	source.copy_out(mc.mfcc_origin);
	group.copy_out(mc.mfcc_mcastgrp);
	mc.mfcc_parent = iif_vif_index;
	for (uint32_t i = 0; i < mfea_node().maxvifs(); i++) {
	    mc.mfcc_ttls[i] = oifs_ttl[i];
#if defined(HAVE_MFCC_FLAGS) && defined(ENABLE_ADVANCED_MULTICAST_API)
	    mc.mfcc_flags[i] = oifs_flags[i];
#endif
	}
#if defined(HAVE_MFCC_RP) && defined(ENABLE_ADVANCED_MULTICAST_API)
	if (_mrt_api_mrt_mfc_rp)
	    rp_addr.copy_out(mc.mfcc_rp);
#endif
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_ADD_MFC,
		       (void *)&mc, sizeof(mc)) < 0) {
	    XLOG_ERROR("setsockopt(MRT_ADD_MFC, (%s, %s)) failed: %s",
		       cstring(source), cstring(group), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    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(HAVE_MF6CCTL2) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct mf6cctl2 mc;
#else
	struct mf6cctl mc;
#endif
	
	memset(&mc, 0, sizeof(mc));
	IF_ZERO(&mc.mf6cc_ifset);	
	source.copy_out(mc.mf6cc_origin);
	group.copy_out(mc.mf6cc_mcastgrp);
	mc.mf6cc_parent = iif_vif_index;
	for (uint32_t i = 0; i < mfea_node().maxvifs(); i++) {
	    if (oifs_ttl[i] > 0)
		IF_SET(i, &mc.mf6cc_ifset);
#if defined(HAVE_MF6CC_FLAGS) && defined(ENABLE_ADVANCED_MULTICAST_API)
	    mc.mf6cc_flags[i] = oifs_flags[i];
#endif
	}
#if defined(HAVE_MF6CC_RP) && defined(ENABLE_ADVANCED_MULTICAST_API)
	if (_mrt_api_mrt_mfc_rp)
	    rp_addr.copy_out(mc.mf6cc_rp);
#endif
	
	if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_ADD_MFC,
		       (void *)&mc, sizeof(mc)) < 0) {
	    XLOG_ERROR("setsockopt(MRT6_ADD_MFC, (%s, %s)) failed: %s",
		       cstring(source), cstring(group), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}


/**
 * MfeaMrouter::delete_mfc:
 * @source: The MFC source address.
 * @group: The MFC group address.
 * 
 * Delete a Multicast Forwarding Cache (MFC) entry in the kernel.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::delete_mfc(const IPvX& source, const IPvX& group)
{
#if !defined(HAVE_IPV4_MULTICAST_ROUTING) && !defined(HAVE_IPV6_MULTICAST_ROUTING)
    UNUSED(source);
    UNUSED(group);
#endif

    XLOG_TRACE(mfea_node().is_log_trace(),
	       "Delete MFC entry: (%s, %s)",
	       cstring(source),
	       cstring(group));

    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("delete_mfc() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct mfcctl mc;
	
	source.copy_out(mc.mfcc_origin);
	group.copy_out(mc.mfcc_mcastgrp);
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_MFC,
		       (void *)&mc, sizeof(mc)) < 0) {
	    XLOG_ERROR("setsockopt(MRT_DEL_MFC, (%s, %s)) failed: %s",
		       cstring(source), cstring(group), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("delete_mfc() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	
	struct mf6cctl mc;

	source.copy_out(mc.mf6cc_origin);
	group.copy_out(mc.mf6cc_mcastgrp);
	
	if (setsockopt(_mrouter_socket, IPPROTO_IPV6, MRT6_DEL_MFC,
		       (void *)&mc, sizeof(mc)) < 0) {
	    XLOG_ERROR("setsockopt(MRT6_DEL_MFC, (%s, %s)) failed: %s",
		       cstring(source), cstring(group), strerror(errno));
	    return (XORP_ERROR);
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaMrouter::add_bw_upcall:
 * @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 in the kernel.
 * 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
MfeaMrouter::add_bw_upcall(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)
{
    XLOG_TRACE(mfea_node().is_log_trace(),
	       "Add dataflow monitor: "
	       "source = %s group = %s "
	       "threshold_interval_sec = %d threshold_interval_usec = %d "
	       "threshold_packets = %d threshold_bytes = %d "
	       "is_threshold_in_packets = %d is_threshold_in_bytes = %d "
	       "is_geq_upcall = %d is_leq_upcall = %d",
	       cstring(source), cstring(group),
	       threshold_interval.sec(), threshold_interval.usec(),
	       threshold_packets, threshold_bytes,
	       is_threshold_in_packets, is_threshold_in_bytes,
	       is_geq_upcall, is_leq_upcall);

#if ! defined(ENABLE_ADVANCED_MULTICAST_API)
    UNUSED(threshold_interval);
    UNUSED(threshold_packets);
    UNUSED(threshold_bytes);
    UNUSED(is_threshold_in_packets);
    UNUSED(is_threshold_in_bytes);
    UNUSED(is_geq_upcall);
    UNUSED(is_leq_upcall);
#endif

    //
    // Check if the kernel supports the bandwidth-upcall mechanism.
    //
    if (! mrt_api_mrt_mfc_bw_upcall()) {
	error_msg = c_format("add_bw_upcall(%s, %s) failed: "
			     "dataflow monitor entry in the kernel "
			     "is not supported",
			     cstring(source), cstring(group));
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }

    // 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
    }
    
    //
    // Do the job
    //
    switch (family()) {
    case AF_INET:
    {
#if defined(MRT_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct bw_upcall bw_upcall;
	
	//
	// Set the argument
	//
	memset(&bw_upcall, 0, sizeof(bw_upcall));
	source.copy_out(bw_upcall.bu_src);
	group.copy_out(bw_upcall.bu_dst);
	threshold_interval.copy_out(bw_upcall.bu_threshold.b_time);
	bw_upcall.bu_threshold.b_packets = threshold_packets;
	bw_upcall.bu_threshold.b_bytes = threshold_bytes;
	if (is_threshold_in_packets)
	    bw_upcall.bu_flags |= BW_UPCALL_UNIT_PACKETS;
	if (is_threshold_in_bytes)
	    bw_upcall.bu_flags |= BW_UPCALL_UNIT_BYTES;
	do {
	    if (is_geq_upcall) {
		bw_upcall.bu_flags |= BW_UPCALL_GEQ;
		break;
	    }
	    if (is_leq_upcall) {
		bw_upcall.bu_flags |= BW_UPCALL_LEQ;
		break;
	    }
	    XLOG_UNREACHABLE();
	    return (XORP_ERROR);
	} while (false);
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_ADD_BW_UPCALL,
		       (void *)&bw_upcall, sizeof(bw_upcall)) < 0) {
	    error_msg = c_format("setsockopt(MRT_ADD_BW_UPCALL, (%s, %s)) failed: %s",
				 cstring(source), cstring(group),
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
#endif // MRT_ADD_BW_UPCALL && ENABLE_ADVANCED_MULTICAST_API
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	error_msg = c_format("add_bw_upcall() failed: "
			     "IPv6 multicast routing not supported");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
#else
	
#if defined(MRT6_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct bw6_upcall bw_upcall;
	
	//
	// Set the argument
	//
	memset(&bw_upcall, 0, sizeof(bw_upcall));
	source.copy_out(bw_upcall.bu6_src);
	group.copy_out(bw_upcall.bu6_dst);
	threshold_interval.copy_out(bw_upcall.bu6_threshold.b_time);
	bw_upcall.bu6_threshold.b_packets = threshold_packets;
	bw_upcall.bu6_threshold.b_bytes = threshold_bytes;
	if (is_threshold_in_packets)
	    bw_upcall.bu6_flags |= BW_UPCALL_UNIT_PACKETS;
	if (is_threshold_in_bytes)
	    bw_upcall.bu6_flags |= BW_UPCALL_UNIT_BYTES;
	do {
	    if (is_geq_upcall) {
		bw_upcall.bu6_flags |= BW_UPCALL_GEQ;
		break;
	    }
	    if (is_leq_upcall) {
		bw_upcall.bu6_flags |= BW_UPCALL_LEQ;
		break;
	    }
	    XLOG_UNREACHABLE();
	    return (XORP_ERROR);
	} while (false);
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT6_ADD_BW_UPCALL,
		       (void *)&bw_upcall, sizeof(bw_upcall)) < 0) {
	    error_msg = c_format("setsockopt(MRT6_ADD_BW_UPCALL, (%s, %s)) failed: %s",
				 cstring(source), cstring(group),
				 strerror(errno));
	    XLOG_ERROR(("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
#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);
}

/**
 * MfeaMrouter::delete_bw_upcall:
 * @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 from the kernel.
 * 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
MfeaMrouter::delete_bw_upcall(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)
{
    XLOG_TRACE(mfea_node().is_log_trace(),
	       "Delete dataflow monitor: "
	       "source = %s group = %s "
	       "threshold_interval_sec = %d threshold_interval_usec = %d "
	       "threshold_packets = %d threshold_bytes = %d "
	       "is_threshold_in_packets = %d is_threshold_in_bytes = %d "
	       "is_geq_upcall = %d is_leq_upcall = %d",
	       cstring(source), cstring(group),
	       threshold_interval.sec(), threshold_interval.usec(),
	       threshold_packets, threshold_bytes,
	       is_threshold_in_packets, is_threshold_in_bytes,
	       is_geq_upcall, is_leq_upcall);

#if ! defined(ENABLE_ADVANCED_MULTICAST_API)
    UNUSED(threshold_interval);
    UNUSED(threshold_packets);
    UNUSED(threshold_bytes);
    UNUSED(is_threshold_in_packets);
    UNUSED(is_threshold_in_bytes);
    UNUSED(is_geq_upcall);
    UNUSED(is_leq_upcall);
#endif

    //
    // Check if the kernel supports the bandwidth-upcall mechanism.
    //
    if (! mrt_api_mrt_mfc_bw_upcall()) {
	error_msg = c_format("add_bw_upcall(%s, %s) failed: "
			     "dataflow monitor entry in the kernel "
			     "is not supported",
			     cstring(source), cstring(group));
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }

    // 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
    }
    
    //
    // Do the job
    //
    switch (family()) {
    case AF_INET:
    {
#if defined(MRT_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct bw_upcall bw_upcall;
	
	//
	// Set the argument
	//
	memset(&bw_upcall, 0, sizeof(bw_upcall));
	source.copy_out(bw_upcall.bu_src);
	group.copy_out(bw_upcall.bu_dst);
	threshold_interval.copy_out(bw_upcall.bu_threshold.b_time);
	bw_upcall.bu_threshold.b_packets = threshold_packets;
	bw_upcall.bu_threshold.b_bytes = threshold_bytes;
	if (is_threshold_in_packets)
	    bw_upcall.bu_flags |= BW_UPCALL_UNIT_PACKETS;
	if (is_threshold_in_bytes)
	    bw_upcall.bu_flags |= BW_UPCALL_UNIT_BYTES;
	do {
	    if (is_geq_upcall) {
		bw_upcall.bu_flags |= BW_UPCALL_GEQ;
		break;
	    }
	    if (is_leq_upcall) {
		bw_upcall.bu_flags |= BW_UPCALL_LEQ;
		break;
	    }
	    XLOG_UNREACHABLE();
	    return (XORP_ERROR);
	} while (false);
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_BW_UPCALL,
		       (void *)&bw_upcall, sizeof(bw_upcall)) < 0) {
	    error_msg = c_format("setsockopt(MRT_DEL_BW_UPCALL, (%s, %s)) failed: %s",
				 cstring(source), cstring(group),
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
#endif // MRT_ADD_BW_UPCALL && ENABLE_ADVANCED_MULTICAST_API
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	error_msg = ("delete_bw_upcall() failed: "
		     "IPv6 multicast routing not supported");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
#else
	
#if defined(MRT6_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct bw6_upcall bw_upcall;
	
	//
	// Set the argument
	//
	memset(&bw_upcall, 0, sizeof(bw_upcall));
	source.copy_out(bw_upcall.bu6_src);
	group.copy_out(bw_upcall.bu6_dst);
	threshold_interval.copy_out(bw_upcall.bu6_threshold.b_time);
	bw_upcall.bu6_threshold.b_packets = threshold_packets;
	bw_upcall.bu6_threshold.b_bytes = threshold_bytes;
	if (is_threshold_in_packets)
	    bw_upcall.bu6_flags |= BW_UPCALL_UNIT_PACKETS;
	if (is_threshold_in_bytes)
	    bw_upcall.bu6_flags |= BW_UPCALL_UNIT_BYTES;
	do {
	    if (is_geq_upcall) {
		bw_upcall.bu6_flags |= BW_UPCALL_GEQ;
		break;
	    }
	    if (is_leq_upcall) {
		bw_upcall.bu6_flags |= BW_UPCALL_LEQ;
		break;
	    }
	    XLOG_UNREACHABLE();
	    return (XORP_ERROR);
	} while (false);
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT6_DEL_BW_UPCALL,
		       (void *)&bw_upcall, sizeof(bw_upcall)) < 0) {
	    error_msg = c_format("setsockopt(MRT6_DEL_BW_UPCALL, (%s, %s)) failed: %s",
				 cstring(source), cstring(group),
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
#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);
}

/**
 * MfeaMrouter::delete_all_bw_upcall:
 * @source: The source address.
 * @group: The group address.
 * @error_msg: The error message (if error).
 * 
 * Delete all dataflow monitor entries from the kernel
 * for a given @source and @group address.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::delete_all_bw_upcall(const IPvX& source, const IPvX& group,
				  string& error_msg)
{
    XLOG_TRACE(mfea_node().is_log_trace(),
	       "Delete all dataflow monitors: "
	       "source = %s group = %s",
	       cstring(source), cstring(group));

    //
    // Check if the kernel supports the bandwidth-upcall mechanism.
    //
    if (! mrt_api_mrt_mfc_bw_upcall()) {
	error_msg = c_format("add_bw_upcall(%s, %s) failed: "
			     "dataflow monitor entry in the kernel "
			     "is not supported",
			     cstring(source), cstring(group));
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    //
    // Do the job
    //
    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	error_msg = c_format("delete_all_bw_upcall() failed: "
			     "IPv4 multicast routing not supported");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
#else

#if defined(MRT_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct bw_upcall bw_upcall;
	
	//
	// Set the argument
	//
	memset(&bw_upcall, 0, sizeof(bw_upcall));
	source.copy_out(bw_upcall.bu_src);
	group.copy_out(bw_upcall.bu_dst);
	bw_upcall.bu_flags |= BW_UPCALL_DELETE_ALL;
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT_DEL_BW_UPCALL,
		       (void *)&bw_upcall, sizeof(bw_upcall)) < 0) {
	    error_msg = c_format("setsockopt(MRT_DEL_BW_UPCALL, (%s, %s)) failed: %s",
				 cstring(source), cstring(group),
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
#endif // MRT_ADD_BW_UPCALL && ENABLE_ADVANCED_MULTICAST_API

#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	error_msg = c_format("delete_all_bw_upcall() failed: "
			     "IPv6 multicast routing not supported");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
#else
	
#if defined(MRT6_ADD_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	struct bw6_upcall bw_upcall;
	
	//
	// Set the argument
	//
	memset(&bw_upcall, 0, sizeof(bw_upcall));
	source.copy_out(bw_upcall.bu6_src);
	group.copy_out(bw_upcall.bu6_dst);
	bw_upcall.bu6_flags |= BW_UPCALL_DELETE_ALL;
	
	if (setsockopt(_mrouter_socket, IPPROTO_IP, MRT6_DEL_BW_UPCALL,
		       (void *)&bw_upcall, sizeof(bw_upcall)) < 0) {
	    error_msg = c_format("setsockopt(MRT6_DEL_BW_UPCALL, (%s, %s)) failed: %s",
				 cstring(source), cstring(group),
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
#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);
}

/**
 * MfeaMrouter::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.
 * 
 * Get various counters per (S,G) entry.
 * 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
MfeaMrouter::get_sg_count(const IPvX& source, const IPvX& group,
			  SgCount& sg_count)
{
#if !defined(HAVE_IPV4_MULTICAST_ROUTING) && !defined(HAVE_IPV6_MULTICAST_ROUTING)
    UNUSED(source);
    UNUSED(group);
    UNUSED(sg_count);
#endif

    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("get_sg_count() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct sioc_sg_req sgreq;
	
	memset(&sgreq, 0, sizeof(sgreq));
	source.copy_out(sgreq.src);
	group.copy_out(sgreq.grp);
	//
	// XXX: some older mcast code has bug in ip_mroute.c, get_sg_cnt():
	// the return code is always 0, so this is why we need to check
	// if all values are 0xffffffffU (the indication for error).
	// TODO: remove the 0xffffffffU check in the future.
	//
	if ((ioctl(_mrouter_socket, SIOCGETSGCNT, &sgreq) < 0)
	    || ((sgreq.pktcnt == 0xffffffffU)
		&& (sgreq.bytecnt == 0xffffffffU)
		&& (sgreq.wrong_if == 0xffffffffU))) {
	    XLOG_ERROR("ioctl(SIOCGETSGCNT, (%s %s)) failed: %s",
		       cstring(source), cstring(group), strerror(errno));
	    sg_count.set_pktcnt(~0U);
	    sg_count.set_bytecnt(~0U);
	    sg_count.set_wrong_if(~0U);
	    return (XORP_ERROR);
	}
	sg_count.set_pktcnt(sgreq.pktcnt);
	sg_count.set_bytecnt(sgreq.bytecnt);
	sg_count.set_wrong_if(sgreq.wrong_if);
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("get_sg_count() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct sioc_sg_req6 sgreq;

	memset(&sgreq, 0, sizeof(sgreq));
	source.copy_out(sgreq.src);
	group.copy_out(sgreq.grp);
	if (ioctl(_mrouter_socket, SIOCGETSGCNT_IN6, &sgreq) < 0) {
	    XLOG_ERROR("ioctl(SIOCGETSGCNT_IN6, (%s %s)) failed: %s",
		       cstring(source), cstring(group), strerror(errno));
	    sg_count.set_pktcnt(~0U);
	    sg_count.set_bytecnt(~0U);
	    sg_count.set_wrong_if(~0U);
	    return (XORP_ERROR);
	}
	sg_count.set_pktcnt(sgreq.pktcnt);
	sg_count.set_bytecnt(sgreq.bytecnt);
	sg_count.set_wrong_if(sgreq.wrong_if);
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}


/**
 * MfeaMrouter::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 various counters per virtual interface.
 * 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
MfeaMrouter::get_vif_count(uint32_t vif_index, VifCount& vif_count)
{
    MfeaVif *mfea_vif = mfea_node().vif_find_by_vif_index(vif_index);
    
    if (mfea_vif == NULL)
	return (XORP_ERROR);

#if !defined(HAVE_IPV4_MULTICAST_ROUTING) && !defined(HAVE_IPV6_MULTICAST_ROUTING)
    UNUSED(vif_count);
#endif
    
    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("get_vif_count() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct sioc_vif_req vreq;

	memset(&vreq, 0, sizeof(vreq));
	vreq.vifi = mfea_vif->vif_index();
	if (ioctl(_mrouter_socket, SIOCGETVIFCNT, &vreq) < 0) {
	    XLOG_ERROR("ioctl(SIOCGETVIFCNT, vif %s) failed: %s",
		       mfea_vif->name().c_str(), strerror(errno));
	    vif_count.set_icount(~0U);
	    vif_count.set_ocount(~0U);
	    vif_count.set_ibytes(~0U);
	    vif_count.set_obytes(~0U);
	    return (XORP_ERROR);
	}
	vif_count.set_icount(vreq.icount);
	vif_count.set_ocount(vreq.ocount);
	vif_count.set_ibytes(vreq.ibytes);
	vif_count.set_obytes(vreq.obytes);
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("get_vif_count() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct sioc_mif_req6 mreq;
	
	memset(&mreq, 0, sizeof(mreq));
	mreq.mifi = mfea_vif->vif_index();
	if (ioctl(_mrouter_socket, SIOCGETMIFCNT_IN6, &mreq) < 0) {
	    XLOG_ERROR("ioctl(SIOCGETMIFCNT_IN6, vif %s) failed: %s",
		       mfea_vif->name().c_str(), strerror(errno));
	    vif_count.set_icount(~0U);
	    vif_count.set_ocount(~0U);
	    vif_count.set_ibytes(~0U);
	    vif_count.set_obytes(~0U);
	    return (XORP_ERROR);
	}
	vif_count.set_icount(mreq.icount);
	vif_count.set_ocount(mreq.ocount);
	vif_count.set_ibytes(mreq.ibytes);
	vif_count.set_obytes(mreq.obytes);
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * MfeaMrouter::mrouter_socket_read:
 * @fd: file descriptor of arriving data.
 * @type: The event type that describes the status of @fd.
 * 
 * Read data from the mrouter socket, and then call the appropriate method
 * to process it.
 **/
void
MfeaMrouter::mrouter_socket_read(XorpFd fd, IoEventType type)
{
    ssize_t nbytes;
    
    UNUSED(fd);
    UNUSED(type);
 
#ifndef HOST_OS_WINDOWS
    // Zero and reset various fields
    _rcvmh.msg_controllen = CMSG_BUF_SIZE;

    // TODO: when resetting _from4 and _from6 do we need to set the address
    // family and the sockaddr len?
    switch (family()) {
    case AF_INET:
	memset(&_from4, 0, sizeof(_from4));
	_rcvmh.msg_namelen = sizeof(_from4);
	break;

#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_FATAL("mrouter_socket_read() failed: "
		   "IPv6 multicast routing not supported");
	return;
#else
	memset(&_from6, 0, sizeof(_from6));
	_rcvmh.msg_namelen = sizeof(_from6);
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6

    default:
	XLOG_UNREACHABLE();
	return;			// Error
    }

    // Read from the socket
    nbytes = recvmsg(_mrouter_socket, &_rcvmh, 0);
    if (nbytes < 0) {
	if (errno == EINTR)
	    return;		// OK: restart receiving
	XLOG_ERROR("recvmsg() on socket %p failed: %s",
		   _mrouter_socket.str().c_str(), strerror(errno));
	return;			// Error
    }

#else // HOST_OS_WINDOWS
    UNUSED(nbytes);
    XLOG_FATAL("Multicast routing is not supported on Windows");
#endif
    
    // Check if it is a signal from the kernel to the user-level
    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_FATAL("mrouter_socket_read() failed: "
		   "IPv4 multicast routing not supported");
	return;
#else
	if (nbytes < (ssize_t)sizeof(struct igmpmsg)) {
	    XLOG_WARNING("mrouter_socket_read() failed: "
			 "kernel signal packet size %d is smaller than minimum size %u",
			 XORP_INT_CAST(nbytes),
			 XORP_UINT_CAST(sizeof(struct igmpmsg)));
	    return;		// Error
	}
	struct igmpmsg igmpmsg;
	memcpy(&igmpmsg, _rcvbuf0, sizeof(igmpmsg));
	if (igmpmsg.im_mbz == 0) {
	    //
	    // XXX: Packets sent up from kernel to daemon have
	    //      igmpmsg.im_mbz = ip->ip_p = 0
	    //
	    kernel_call_process(_rcvbuf0, nbytes);
	    return;		// OK
	}
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_FATAL("mrouter_socket_read() failed: "
		   "IPv6 multicast routing not supported");
	return;
#else
	if ((nbytes < (ssize_t)sizeof(struct mrt6msg))
	    && (nbytes < (ssize_t)MLD_MINLEN)) {
	    XLOG_WARNING("mrouter_socket_read() failed: "
			 "kernel signal packet size %d is smaller than minimum size %u",
			 XORP_INT_CAST(nbytes),
			 XORP_UINT_CAST(min(sizeof(struct mrt6msg),
					    (size_t)MLD_MINLEN)));
	    return;		// Error
	}
	struct mrt6msg mrt6msg;
	memcpy(&mrt6msg, _rcvbuf0, sizeof(mrt6msg));
	if ((mrt6msg.im6_mbz == 0) || (_rcvmh.msg_controllen == 0)) {
	    //
	    // XXX: Packets sent up from kernel to daemon have
	    //      mrt6msg.im6_mbz = icmp6_hdr->icmp6_type = 0
	    // Because we set ICMP6 filters on the socket,
	    // we should never see a real ICMPv6 packet
	    // with icmp6_type = 0 .
	    //
	    //
	    // TODO: XXX: (msg_controllen == 0) is presumably
	    // true for older IPv6 systems (e.g. KAME circa
	    // April 2000, FreeBSD-4.0) which don't have the
	    //     'icmp6_type = 0' mechanism.
	    //
	    kernel_call_process(_rcvbuf0, nbytes);
	    return;		// OK
	}
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	break;
    }
    
    //
    // Not a kernel signal. Ignore it.
    //
    
    return;			// OK
}

/**
 * kernel_call_process:
 * @databuf: The data buffer.
 * @datalen: The length of the data in 'databuf'.
 * 
 * Process a call from the kernel (e.g., "nocache", "wrongiif", "wholepkt")
 * XXX: It is OK for im_src/im6_src to be 0 (for 'nocache' or 'wrongiif'),
 *	just in case the kernel supports (*,G) MFC.
 * 
 * Return value: %XORP_OK on success, otherwise %XORP_ERROR.
 **/
int
MfeaMrouter::kernel_call_process(uint8_t *databuf, size_t datalen)
{
    uint32_t	iif_vif_index;
    int		message_type;
    IPvX	src(family()), dst(family());

#if !defined(HAVE_IPV4_MULTICAST_ROUTING) && !defined(HAVE_IPV6_MULTICAST_ROUTING)
    UNUSED(iif_vif_index);
    UNUSED(message_type);
    UNUSED(databuf);
    UNUSED(datalen);
#endif
    
    switch (family()) {
    case AF_INET:
    {
#ifndef HAVE_IPV4_MULTICAST_ROUTING
	XLOG_ERROR("kernel_call_process() failed: "
		   "IPv4 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct igmpmsg igmpmsg;
	memcpy(&igmpmsg, databuf, sizeof(igmpmsg));
	
	//
	// Get the message type, the iif, and source and destination address
	//
	message_type = igmpmsg.im_msgtype;
	iif_vif_index = igmpmsg.im_vif;
	if (message_type == IGMPMSG_WHOLEPKT) {
	    //
	    // If a WHOLEPKT message, then get the inner source and
	    // destination addresses
	    //
	    IpHeader4 ip4(databuf + sizeof(struct igmpmsg));
	    if (datalen - sizeof(struct igmpmsg) < ip4.size()) {
		// The inner packet is too small
		return (XORP_ERROR);
	    }
	    src = ip4.ip_src();
	    dst = ip4.ip_dst();
	} else {
	    src.copy_in(igmpmsg.im_src);
	    dst.copy_in(igmpmsg.im_dst);
	}

	//
	// Check if the iif is valid and UP
	//
	switch (message_type) {
	case IGMPMSG_NOCACHE:
	case IGMPMSG_WRONGVIF:
	case IGMPMSG_WHOLEPKT:
	{
	    MfeaVif *mfea_vif = mfea_node().vif_find_by_vif_index(iif_vif_index);
	    if ((mfea_vif == NULL) || (! mfea_vif->is_up())) {
		// Silently ignore the packet
		return (XORP_ERROR);
	    }
	}
	break;
#if defined(IGMPMSG_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	case IGMPMSG_BW_UPCALL:
	    break;
#endif
	default:
	    break;
	}
	
	//
	// Check 'src' and 'dst' addresses
	//
	switch (message_type) {
	case IGMPMSG_NOCACHE:
	case IGMPMSG_WRONGVIF:
	case IGMPMSG_WHOLEPKT:
	    if ((! src.is_unicast())
		|| (! dst.is_multicast())
		|| (dst.is_linklocal_multicast())) {
		// XXX: LAN-scoped addresses are not routed
		return (XORP_ERROR);
	    }
	    break;
#if defined(IGMPMSG_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	case IGMPMSG_BW_UPCALL:
	    break;
#endif
	default:
	    break;
	}
	//
	// Deliver the signal
	//
	mfea_node().signal_message_recv("",
					module_id(),
					message_type,
					iif_vif_index, src, dst,
					databuf + sizeof(struct igmpmsg),
					datalen - sizeof(struct igmpmsg));
#endif // HAVE_IPV4_MULTICAST_ROUTING
    }
    break;
    
#ifdef HAVE_IPV6
    case AF_INET6:
    {
#ifndef HAVE_IPV6_MULTICAST_ROUTING
	XLOG_ERROR("kernel_call_process() failed: "
		   "IPv6 multicast routing not supported");
	return (XORP_ERROR);
#else
	struct mrt6msg mrt6msg;
	memcpy(&mrt6msg, databuf, sizeof(mrt6msg));
	//
	// Get the message type, the iif, and source and destination address
	//
	message_type = mrt6msg.im6_msgtype;
	iif_vif_index = mrt6msg.im6_mif;
	if (message_type == MRT6MSG_WHOLEPKT) {
	    //
	    // If a WHOLEPKT message, then get the inner source and
	    // destination addresses
	    //
	    IpHeader6 ip6(databuf + sizeof(struct mrt6msg));
	    if (datalen - sizeof(struct mrt6msg) < ip6.size()) {
		// The inner packet is too small
		return (XORP_ERROR);
	    }
	    src = ip6.ip_src();
	    dst = ip6.ip_dst();
	} else {
	    src.copy_in(mrt6msg.im6_src);
	    dst.copy_in(mrt6msg.im6_dst);
	}
	
	//
	// Check if the iif is valid and UP
	//
	switch (message_type) {
	case MRT6MSG_NOCACHE:
	case MRT6MSG_WRONGMIF:
	case MRT6MSG_WHOLEPKT:
	{
	    // Check if the iif is valid and UP
	    MfeaVif *mfea_vif = mfea_node().vif_find_by_vif_index(iif_vif_index);
	    if ((mfea_vif == NULL) || (! mfea_vif->is_up())) {
		// Silently ignore the packet
		return (XORP_ERROR);
	    }
	}
	break;
#if defined(MRT6MSG_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	case MRT6MSG_BW_UPCALL:
	    break;
#endif
	default:
	    break;
	}
	
	//
	// Check 'src' and 'dst' addresses
	//
	switch (message_type) {
	case MRT6MSG_NOCACHE:
	case MRT6MSG_WRONGMIF:
	case MRT6MSG_WHOLEPKT:
	    if ((! src.is_unicast())
		|| (! dst.is_multicast())
		|| dst.is_linklocal_multicast()) {
		// XXX: LAN-scoped addresses are not routed
		return (XORP_ERROR);
	    }
	    break;
#if defined(MRT6MSG_BW_UPCALL) && defined(ENABLE_ADVANCED_MULTICAST_API)
	case MRT6MSG_BW_UPCALL:
	    break;
#endif
	default:
	    break;
	}
	//
	// Deliver the signal
	//
	mfea_node().signal_message_recv("",
					module_id(),
					message_type,
					iif_vif_index, src, dst,
					databuf + sizeof(struct mrt6msg),
					datalen - sizeof(struct mrt6msg));
#endif // HAVE_IPV6_MULTICAST_ROUTING
    }
    break;
#endif // HAVE_IPV6
    
    default:
	XLOG_UNREACHABLE();
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}


syntax highlighted by Code2HTML, v. 0.9.1