// -*- 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_vif.cc,v 1.16 2007/02/16 22:45:47 pavlin Exp $"
//
// MFEA virtual interfaces implementation.
//
#include "mfea_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/ipvx.hh"
#include "mrt/multicast_defs.h"
#include "mfea_node.hh"
#include "mfea_osdep.hh"
#include "mfea_vif.hh"
//
// Exported variables
//
//
// Local constants definitions
//
//
// Local structures/classes, typedefs and macros
//
//
// Local variables
//
//
// Local functions prototypes
//
/**
* MfeaVif::MfeaVif:
* @mfea_node: The MFEA node this interface belongs to.
* @vif: The generic Vif interface that contains various information.
*
* MFEA vif constructor.
**/
MfeaVif::MfeaVif(MfeaNode& mfea_node, const Vif& vif)
: ProtoUnit(mfea_node.family(), mfea_node.module_id()),
Vif(vif),
_mfea_node(mfea_node)
{
_min_ttl_threshold = MINTTL;
_max_rate_limit = 0; // XXX: unlimited rate limit
}
/**
* MfeaVif::MfeaVif:
* @mfea_node: The MFEA node this interface belongs to.
* @mfea_vif: The origin MfeaVif interface that contains the initialization
* information.
*
* MFEA vif copy constructor.
**/
MfeaVif::MfeaVif(MfeaNode& mfea_node, const MfeaVif& mfea_vif)
: ProtoUnit(mfea_node.family(), mfea_node.module_id()),
Vif(mfea_vif),
_mfea_node(mfea_node)
{
// Copy the MfeaVif-specific information
set_min_ttl_threshold(mfea_vif.min_ttl_threshold());
set_max_rate_limit(mfea_vif.max_rate_limit());
}
/**
* MfeaVif::~MfeaVif:
* @:
*
* MFEA vif destructor.
*
**/
MfeaVif::~MfeaVif()
{
string error_msg;
stop(error_msg);
}
/**
* MfeaVif::start:
* @error_msg: The error message (if error).
*
* Start MFEA on a single virtual interface.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
MfeaVif::start(string& error_msg)
{
if (! is_enabled())
return (XORP_OK);
if (is_up() || is_pending_up())
return (XORP_OK);
if (! is_underlying_vif_up()) {
error_msg = "underlying vif is not UP";
return (XORP_ERROR);
}
//
// Install in the kernel only if the vif is of the appropriate type:
// multicast-capable (loopback excluded), or PIM Register vif.
//
if (! ((is_multicast_capable() && (! is_loopback()))
|| is_pim_register())) {
error_msg = "the interface is not multicast capable";
return (XORP_ERROR);
}
if (ProtoUnit::start() < 0) {
error_msg = "internal error";
return (XORP_ERROR);
}
if (mfea_node().add_multicast_vif(vif_index()) < 0) {
error_msg = "cannot add the multicast vif to the kernel";
return (XORP_ERROR);
}
XLOG_INFO("Interface started: %s%s",
this->str().c_str(), flags_string().c_str());
return (XORP_OK);
}
/**
* MfeaVif::stop:
* @error_msg: The error message (if error).
*
* Stop MFEA on a single virtual interface.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
MfeaVif::stop(string& error_msg)
{
int ret_value = XORP_OK;
if (is_down())
return (XORP_OK);
if (! (is_up() || is_pending_up() || is_pending_down())) {
error_msg = "the vif state is not UP or PENDING_UP or PENDING_DOWN";
return (XORP_ERROR);
}
if (ProtoUnit::pending_stop() < 0) {
error_msg = "internal error";
ret_value = XORP_ERROR;
}
leave_all_multicast_groups();
if (ProtoUnit::stop() < 0) {
error_msg = "internal error";
ret_value = XORP_ERROR;
}
if (mfea_node().delete_multicast_vif(vif_index()) < 0) {
XLOG_ERROR("Cannot delete multicast vif %s with the kernel",
name().c_str());
ret_value = XORP_ERROR;
}
XLOG_INFO("Interface stopped %s%s",
this->str().c_str(), flags_string().c_str());
//
// Inform the node that the vif has completed the shutdown
//
mfea_node().vif_shutdown_completed(name());
return (ret_value);
}
/**
* Enable MFEA on a single virtual interface.
*
* If an unit is not enabled, it cannot be start, or pending-start.
*/
void
MfeaVif::enable()
{
ProtoUnit::enable();
XLOG_INFO("Interface enabled %s%s",
this->str().c_str(), flags_string().c_str());
}
/**
* Disable MFEA on a single virtual interface.
*
* If an unit is disabled, it cannot be start or pending-start.
* If the unit was runnning, it will be stop first.
*/
void
MfeaVif::disable()
{
string error_msg;
stop(error_msg);
ProtoUnit::disable();
XLOG_INFO("Interface disabled %s%s",
this->str().c_str(), flags_string().c_str());
}
/**
* MfeaVif::start_protocol:
* @module_instance_name: The module instance name of the protocol to start
* on this vif.
* @module_id: The #xorp_module_id of the protocol to start on this vif.
*
* Start a protocol on a single virtual vif.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
MfeaVif::start_protocol(const string& module_instance_name,
xorp_module_id module_id)
{
if (! is_valid_module_id(module_id)) {
XLOG_ERROR("Cannot start protocol instance %s on vif %s: "
"invalid module_id = %d",
module_instance_name.c_str(), name().c_str(), module_id);
return (XORP_ERROR);
}
if (_proto_register.add_protocol(module_instance_name, module_id) < 0)
return (XORP_ERROR);
//
// Start the protocol in the MfeaNode
// (XXX: may be called more than once)
if (mfea_node().start_protocol(module_id) < 0) {
_proto_register.delete_protocol(module_instance_name, module_id);
return (XORP_ERROR);
}
// TODO: should we implicitly start the vif? Maybe no, because is bad
// semantics...?
#if 0
string error_msg;
if (! is_up())
start(error_msg); // XXX: start the vif
#endif // 0
return (XORP_OK);
}
/**
* MfeaVif::stop_protocol:
* @module_instance_name: The module instance name of the protocol to stop
* on this vif.
* @module_id: The #xorp_module_id of the protocol to stop on this vif.
*
* Stop a protocol on this vif.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
MfeaVif::stop_protocol(const string& module_instance_name,
xorp_module_id module_id)
{
if (! is_valid_module_id(module_id)) {
XLOG_ERROR("Cannot stop protocol instance %s on vif %s: "
"invalid module_id = %d",
module_instance_name.c_str(), name().c_str(), module_id);
return (XORP_ERROR);
}
leave_all_multicast_groups(module_instance_name, module_id);
if (_proto_register.delete_protocol(module_instance_name, module_id) < 0)
return (XORP_ERROR);
// TODO: should we implicitly stop the vif? Maybe no, because is bad
// semantics...?
#if 0
//
// Test if we should stop the vif
//
bool do_stop = true;
for (size_t i = 0; i < XORP_MODULE_MAX; i++) {
if (_proto_register.is_registered(static_cast<xorp_module_id>(i))) {
do_stop = false; // Vif is still in use by a protocol
break;
}
}
string error_msg;
if (is_up() && do_stop)
stop(error_msg); // Stop the vif
#endif // 0
return (XORP_OK);
}
/**
* MfeaVif::leave_all_multicast_groups:
*
* Leave all previously joined multicast groups on this interface.
*
* Return value: The number of groups that were successfully left,
* or %XORP_ERROR if error.
**/
int
MfeaVif::leave_all_multicast_groups()
{
int ret_value = 0;
// Get a copy of the joined multicast state
list<pair<pair<string, xorp_module_id>, IPvX> > joined_state
= _joined_groups.joined_state();
list<pair<pair<string, xorp_module_id>, IPvX> >::iterator iter;
// Remove all the joined state and leave the groups
for (iter = joined_state.begin(); iter != joined_state.end(); ++iter) {
pair<string, xorp_module_id>& proto_state = (*iter).first;
string& tmp_module_instance_name = proto_state.first;
xorp_module_id tmp_module_id = proto_state.second;
IPvX& group = (*iter).second;
if (mfea_node().leave_multicast_group(tmp_module_instance_name,
tmp_module_id,
vif_index(),
group) == XORP_OK)
ret_value++;
}
return (ret_value);
}
/**
* MfeaVif::leave_all_multicast_groups:
* @module_instance_name: The module instance name of the protocol that
* leaves all groups.
* @module_id: The module ID of the protocol that leaves all groups.
*
* Leave all previously joined multicast groups on this interface
* that were joined by a module with name @module_instance_name
* and module ID @module_id.
*
* Return value: The number of groups that were successfully left,
* or %XORP_ERROR if error.
**/
int
MfeaVif::leave_all_multicast_groups(const string& module_instance_name,
xorp_module_id module_id)
{
int ret_value = 0;
// Get a copy of the joined multicast state
list<pair<pair<string, xorp_module_id>, IPvX> > joined_state
= _joined_groups.joined_state();
list<pair<pair<string, xorp_module_id>, IPvX> >::iterator iter;
// Remove all the joined state and leave the groups that were joined by
// a module that matches 'module_instance_name' and 'module_id'.
for (iter = joined_state.begin(); iter != joined_state.end(); ++iter) {
pair<string, xorp_module_id>& proto_state = (*iter).first;
string& tmp_module_instance_name = proto_state.first;
xorp_module_id tmp_module_id = proto_state.second;
IPvX& group = (*iter).second;
if ((module_instance_name != tmp_module_instance_name)
|| (module_id != tmp_module_id))
continue;
if (mfea_node().leave_multicast_group(module_instance_name,
module_id,
vif_index(),
group) == XORP_OK)
ret_value++;
}
return (ret_value);
}
/**
* MfeaVif::JoinedGroups::add_multicast_group:
* @module_instance_name: The module instance name of the protocol that
* joins the group.
* @module_id: The module ID of the protocol that joins the group.
* @group: The address of the multicast group to add.
*
* Add a group to the set of joined multicast groups.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
MfeaVif::JoinedGroups::add_multicast_group(const string& module_instance_name,
xorp_module_id module_id,
const IPvX& group)
{
pair<string, xorp_module_id> my_pair(module_instance_name, module_id);
if (find(_joined_state.begin(), _joined_state.end(),
pair<pair<string, xorp_module_id>, IPvX>(my_pair, group))
!= _joined_state.end()) {
return (XORP_ERROR); // Already joined
}
// Add the pair of group address and module instance name
_joined_state.push_back(pair<pair<string, xorp_module_id>, IPvX>(my_pair, group));
//
// Add the group address itself.
// XXX: if the group was added already, the insert will silently fail.
//
_joined_multicast_groups.insert(group);
return (XORP_OK);
}
/**
* MfeaVif::JoinedGroups::delete_multicast_group:
* @module_instance_name: The module instance name of the protocol that
* leaves the group.
* @module_id: The module ID of the protocol that leaves the group.
* @group: The address of the multicast group to delete.
*
* Delete a group from the set of joined multicast groups.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
MfeaVif::JoinedGroups::delete_multicast_group(const string& module_instance_name,
xorp_module_id module_id,
const IPvX& group)
{
pair<string, xorp_module_id> my_pair(module_instance_name, module_id);
list<pair<pair<string, xorp_module_id>, IPvX> >::iterator iter;
iter = find(_joined_state.begin(), _joined_state.end(),
pair<pair<string, xorp_module_id>, IPvX>(my_pair, group));
if (iter == _joined_state.end())
return (XORP_ERROR); // Probably not added before
_joined_state.erase(iter);
//
// Try to find if other instances have joined the same group
//
for (iter = _joined_state.begin(); iter != _joined_state.end(); ++iter) {
pair<pair<string, xorp_module_id>, IPvX>& tmp_pair = *iter;
if (tmp_pair.second == group)
return (XORP_OK); // Group is still in use
}
// Last instance to join the group. Delete.
if (_joined_multicast_groups.erase(group) > 0)
return (XORP_OK); // Deletion successful
return (XORP_ERROR); // Deletion failed (e.g. group not in the set)
}
/**
* MfeaVif::JoinedGroups::has_multicast_group:
* @group: The address of the multicast group to test.
*
* Test if a multicast group belongs to the set of joined multicast groups.
*
* Return value: true if @group belongs to the set of joined multicast groups,
* otherwise false.
**/
bool
MfeaVif::JoinedGroups::has_multicast_group(const IPvX& group) const
{
return (_joined_multicast_groups.find(group)
!= _joined_multicast_groups.end());
}
// TODO: temporary here. Should go to the Vif class after the Vif
// class starts using the Proto class
string
MfeaVif::flags_string() const
{
string flags;
if (is_up())
flags += " UP";
if (is_down())
flags += " DOWN";
if (is_pending_up())
flags += " PENDING_UP";
if (is_pending_down())
flags += " PENDING_DOWN";
if (is_ipv4())
flags += " IPv4";
if (is_ipv6())
flags += " IPv6";
if (is_enabled())
flags += " ENABLED";
if (is_disabled())
flags += " DISABLED";
return (flags);
}
syntax highlighted by Code2HTML, v. 0.9.1