// -*- 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/xrl_fti.cc,v 1.22 2007/02/16 22:45:52 pavlin Exp $" #include "fea_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "xrl_fti.hh" static const char* FTI_MAX_OPS_HIT = "Resource limit on number of operations in a transaction hit."; static const char* FTI_MAX_TRANSACTIONS_HIT = "Resource limit on number of pending transactions hit."; static const char* FTI_BAD_ID = "Expired or invalid transaction id presented."; XrlCmdError XrlFtiTransactionManager::start_transaction(uint32_t& tid) { if (_ftm.start(tid)) return XrlCmdError::OKAY(); return XrlCmdError::COMMAND_FAILED(FTI_MAX_TRANSACTIONS_HIT); } XrlCmdError XrlFtiTransactionManager::commit_transaction(uint32_t tid) { if (_ftm.commit(tid)) { const string& error_msg = _ftm.error(); if (error_msg.empty()) return XrlCmdError::OKAY(); return XrlCmdError::COMMAND_FAILED(error_msg); } return XrlCmdError::COMMAND_FAILED(FTI_BAD_ID); } XrlCmdError XrlFtiTransactionManager::abort_transaction(uint32_t tid) { if (_ftm.abort(tid)) return XrlCmdError::OKAY(); return XrlCmdError::COMMAND_FAILED(FTI_BAD_ID); } XrlCmdError XrlFtiTransactionManager::add(uint32_t tid, const FtiTransactionManager::Operation& op) { uint32_t n_ops; if (_ftm.retrieve_size(tid, n_ops) == false) return XrlCmdError::COMMAND_FAILED(FTI_BAD_ID); if (_max_ops <= n_ops) return XrlCmdError::COMMAND_FAILED(FTI_MAX_OPS_HIT); if (_ftm.add(tid, op)) return XrlCmdError::OKAY(); // // In theory, resource shortage is the only thing that could get us // here. // return XrlCmdError::COMMAND_FAILED("Unknown resource shortage"); } /** * Process a list of IPv4 FIB route changes. * * The FIB route changes come from the underlying system. * * @param fte_list the list of Fte entries to add or delete. */ void XrlFtiTransactionManager::process_fib_changes(const list& fte_list) { map::iterator iter; for (iter = _fib_clients4.begin(); iter != _fib_clients4.end(); ++iter) { FibClient4& fib_client = iter->second; fib_client.activate(fte_list); } } /** * Process a list of IPv6 FIB route changes. * * The FIB route changes come from the underlying system. * * @param fte_list the list of Fte entries to add or delete. */ void XrlFtiTransactionManager::process_fib_changes(const list& fte_list) { map::iterator iter; for (iter = _fib_clients6.begin(); iter != _fib_clients6.end(); ++iter) { FibClient6& fib_client = iter->second; fib_client.activate(fte_list); } } XrlCmdError XrlFtiTransactionManager::add_fib_client4(const string& client_target_name, const bool send_updates, const bool send_resolves) { // Test if we have this client already if (_fib_clients4.find(client_target_name) != _fib_clients4.end()) { string error_msg = c_format("Target %s is already an IPv4 FIB client", client_target_name.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } // Add the client _fib_clients4.insert(make_pair(client_target_name, FibClient4(client_target_name, *this))); FibClient4& fib_client = _fib_clients4.find(client_target_name)->second; fib_client.set_send_updates(send_updates); fib_client.set_send_resolves(send_resolves); // Activate the client list fte_list; if (ftic().get_table4(fte_list) != true) { string error_msg = "Cannot get the IPv4 FIB"; return XrlCmdError::COMMAND_FAILED(error_msg); } fib_client.activate(fte_list); return XrlCmdError::OKAY(); } XrlCmdError XrlFtiTransactionManager::add_fib_client6(const string& client_target_name, const bool send_updates, const bool send_resolves) { // Test if we have this client already if (_fib_clients6.find(client_target_name) != _fib_clients6.end()) { string error_msg = c_format("Target %s is already an IPv6 FIB client", client_target_name.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } // Add the client _fib_clients6.insert(make_pair(client_target_name, FibClient6(client_target_name, *this))); FibClient6& fib_client = _fib_clients6.find(client_target_name)->second; fib_client.set_send_updates(send_updates); fib_client.set_send_resolves(send_resolves); // Activate the client list fte_list; if (ftic().get_table6(fte_list) != true) { string error_msg = "Cannot get the IPv6 FIB"; return XrlCmdError::COMMAND_FAILED(error_msg); } fib_client.activate(fte_list); return XrlCmdError::OKAY(); } XrlCmdError XrlFtiTransactionManager::delete_fib_client4(const string& client_target_name) { map::iterator iter; iter = _fib_clients4.find(client_target_name); if (iter == _fib_clients4.end()) { string error_msg = c_format("Target %s is not an IPv4 FIB client", client_target_name.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } _fib_clients4.erase(iter); return XrlCmdError::OKAY(); } XrlCmdError XrlFtiTransactionManager::delete_fib_client6(const string& client_target_name) { map::iterator iter; iter = _fib_clients6.find(client_target_name); if (iter == _fib_clients6.end()) { string error_msg = c_format("Target %s is not an IPv6 FIB client", client_target_name.c_str()); return XrlCmdError::COMMAND_FAILED(error_msg); } _fib_clients6.erase(iter); return XrlCmdError::OKAY(); } int XrlFtiTransactionManager::send_fib_client_add_route(const string& target_name, const Fte4& fte) { bool success; success = _xrl_fea_fib_client.send_add_route4( target_name.c_str(), fte.net(), fte.nexthop(), fte.ifname(), fte.vifname(), fte.metric(), fte.admin_distance(), string("NOT_SUPPORTED"), fte.xorp_route(), callback(this, &XrlFtiTransactionManager::send_fib_client_add_route4_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFtiTransactionManager::send_fib_client_add_route(const string& target_name, const Fte6& fte) { bool success; success = _xrl_fea_fib_client.send_add_route6( target_name.c_str(), fte.net(), fte.nexthop(), fte.ifname(), fte.vifname(), fte.metric(), fte.admin_distance(), string("NOT_SUPPORTED"), fte.xorp_route(), callback(this, &XrlFtiTransactionManager::send_fib_client_add_route6_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFtiTransactionManager::send_fib_client_delete_route(const string& target_name, const Fte4& fte) { bool success; success = _xrl_fea_fib_client.send_delete_route4( target_name.c_str(), fte.net(), fte.ifname(), fte.vifname(), callback(this, &XrlFtiTransactionManager::send_fib_client_delete_route4_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFtiTransactionManager::send_fib_client_delete_route(const string& target_name, const Fte6& fte) { bool success; success = _xrl_fea_fib_client.send_delete_route6( target_name.c_str(), fte.net(), fte.ifname(), fte.vifname(), callback(this, &XrlFtiTransactionManager::send_fib_client_delete_route6_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFtiTransactionManager::send_fib_client_resolve_route(const string& target_name, const Fte4& fte) { bool success; success = _xrl_fea_fib_client.send_resolve_route4( target_name.c_str(), fte.net(), callback(this, &XrlFtiTransactionManager::send_fib_client_resolve_route4_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } int XrlFtiTransactionManager::send_fib_client_resolve_route(const string& target_name, const Fte6& fte) { bool success; success = _xrl_fea_fib_client.send_resolve_route6( target_name.c_str(), fte.net(), callback(this, &XrlFtiTransactionManager::send_fib_client_resolve_route6_cb, target_name)); if (success) return XORP_OK; else return XORP_ERROR; } void XrlFtiTransactionManager::send_fib_client_add_route4_cb( const XrlError& xrl_error, string target_name) { map::iterator iter; iter = _fib_clients4.find(target_name); if (iter == _fib_clients4.end()) { // The client has probably gone. Silently ignore. return; } FibClient4& fib_client = iter->second; fib_client.send_fib_client_route_change_cb(xrl_error); } void XrlFtiTransactionManager::send_fib_client_add_route6_cb( const XrlError& xrl_error, string target_name) { map::iterator iter; iter = _fib_clients6.find(target_name); if (iter == _fib_clients6.end()) { // The client has probably gone. Silently ignore. return; } FibClient6& fib_client = iter->second; fib_client.send_fib_client_route_change_cb(xrl_error); } void XrlFtiTransactionManager::send_fib_client_delete_route4_cb( const XrlError& xrl_error, string target_name) { map::iterator iter; iter = _fib_clients4.find(target_name); if (iter == _fib_clients4.end()) { // The client has probably gone. Silently ignore. return; } FibClient4& fib_client = iter->second; fib_client.send_fib_client_route_change_cb(xrl_error); } void XrlFtiTransactionManager::send_fib_client_delete_route6_cb( const XrlError& xrl_error, string target_name) { map::iterator iter; iter = _fib_clients6.find(target_name); if (iter == _fib_clients6.end()) { // The client has probably gone. Silently ignore. return; } FibClient6& fib_client = iter->second; fib_client.send_fib_client_route_change_cb(xrl_error); } void XrlFtiTransactionManager::send_fib_client_resolve_route4_cb( const XrlError& xrl_error, string target_name) { map::iterator iter; iter = _fib_clients4.find(target_name); if (iter == _fib_clients4.end()) { // The client has probably gone. Silently ignore. return; } FibClient4& fib_client = iter->second; fib_client.send_fib_client_route_change_cb(xrl_error); } void XrlFtiTransactionManager::send_fib_client_resolve_route6_cb( const XrlError& xrl_error, string target_name) { map::iterator iter; iter = _fib_clients6.find(target_name); if (iter == _fib_clients6.end()) { // The client has probably gone. Silently ignore. return; } FibClient6& fib_client = iter->second; fib_client.send_fib_client_route_change_cb(xrl_error); } template void XrlFtiTransactionManager::FibClient::activate(const list& fte_list) { bool queue_was_empty = _inform_fib_client_queue.empty(); if (fte_list.empty()) return; // Create the queue with the entries to add typename list::const_iterator iter; for (iter = fte_list.begin(); iter != fte_list.end(); ++iter) { const F& fte = *iter; _inform_fib_client_queue.push_back(fte); } // If the queue was empty before, start sending the routes if (queue_was_empty) send_fib_client_route_change(); } template void XrlFtiTransactionManager::FibClient::send_fib_client_route_change() { int success = XORP_ERROR; do { bool ignore_fte = true; if (_inform_fib_client_queue.empty()) return; // No more route changes to send F& fte = _inform_fib_client_queue.front(); // // If FIB route misses and resolution requests were requested to be // heard by the client, then send notifications of such events. // if (_send_resolves && fte.is_unresolved()) { ignore_fte = false; success = _xftm.send_fib_client_resolve_route(_target_name, fte); } // // If FIB updates were requested by the client, then send notification // of a route being added or deleted. // if (_send_updates && !fte.is_unresolved()) { ignore_fte = false; if (!fte.is_deleted()) { // Send notification of a route being added success = _xftm.send_fib_client_add_route(_target_name, fte); } else { // Send notification of a route being deleted success = _xftm.send_fib_client_delete_route(_target_name, fte); } } if (ignore_fte) { // // This entry is not needed hence silently drop it and process the // next one. // _inform_fib_client_queue.pop_front(); continue; } break; } while (true); if (success != XORP_OK) { // // If an error, then start a timer to try again // TODO: XXX: the timer value is hardcoded here!! // _inform_fib_client_queue_timer = eventloop().new_oneoff_after( TimeVal(1, 0), callback(this, &XrlFtiTransactionManager::FibClient::send_fib_client_route_change)); } } template void XrlFtiTransactionManager::FibClient::send_fib_client_route_change_cb( const XrlError& xrl_error) { // If success, then send the next route change if (xrl_error == XrlError::OKAY()) { _inform_fib_client_queue.pop_front(); send_fib_client_route_change(); return; } // // If command failed because the other side rejected it, // then send the next route change. // if (xrl_error == XrlError::COMMAND_FAILED()) { XLOG_ERROR("Error sending route change to %s: %s", _target_name.c_str(), xrl_error.str().c_str()); _inform_fib_client_queue.pop_front(); send_fib_client_route_change(); return; } // // If a transport error, then start a timer to try again // (unless the timer is already running). // TODO: XXX: the timer value is hardcoded here!! // if (_inform_fib_client_queue_timer.scheduled()) return; _inform_fib_client_queue_timer = eventloop().new_oneoff_after( TimeVal(1, 0), callback(this, &XrlFtiTransactionManager::FibClient::send_fib_client_route_change)); } template class XrlFtiTransactionManager::FibClient; template class XrlFtiTransactionManager::FibClient;