// -*- 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(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::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::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::pending_start() < 0) return (XORP_ERROR); // // Set the node status // ProtoNode::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::start() < 0) { ProtoNode::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::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::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::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::state_str().c_str()); return; } ProtoNode::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::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::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 delete_addresses_list; list::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::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 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::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::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::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::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::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::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::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::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 vif_names; vector::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::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::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::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::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 >& module_list = pr.all_module_instance_name_list(); list >::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 >& module_list = pr.all_module_instance_name_list(); list >::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); }